哈希函数和数字签名

1. 哈希函数 Hash

1.1 概念

  • Hash,一般翻译做散列,也有直接音译为哈希的。就是把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。
  • 这种转换是一种压缩映射,也就是散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说,就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
  • 单向散列函数:又称单向Hash函数、杂凑函数,就是把任意长的输入消息串变化成固定长的输出串且由输出串难以得到输入串的一种函数。这个输出串称为该消息的散列值。一般用于产生消息摘要,密钥加密等.

1.2 特点

  • 算法是公开的
  • 对相同数据运算,得到的结果是一样的
  • 对不同数据运算,如MD5得到的结果默认是128位,32个字符(16进制标识)。
  • 没法进行逆运算信息摘要。
  • 信息“指纹”,是用来做数据识别的。
  • 加密后密文的长度是定长的

1.3 用途

  • 用户密码的加密
  • 搜索引擎,关键字识别(搜索多个关键字,先对每个关键字进行散列,然后多个关键字进行或运算,如果值一致则搜索结果一致)
  • 版权标注 对文件进行散列判断该文件是否是正版或原版的
  • 数字签名 (文件完整性验证 对整个文件进行散列,比较散列值判断文件是否完整或被篡改)
More...

论文学习 - Bitcoin:A Peer-to-Peer Electronic Cash System(2)

比特币:一个点对点的电子货币系统

2. 交易

II. Transactions
We define an electronic coin as a chain of digital signatures. Each owner transfers the coin to the next by digitally signing a hash of the previous transaction and the public key of the next owner and adding these to the end of the coin. A payee can verify the signatures to verify the chain of ownership.

The problem of course is the payee can’t verify that one of the owners did not double-spend the coin. A common solution is to introduce a trusted central authority, or mint, that checks every transaction for double spending. After each transaction, the coin must be returned to the mint to issue a new coin, and only coins issued directly from the mint are trusted not to be double-spent. The problem with this solution is that the fate of the entire money system depends on the company running the mint, with every transaction having to go through them, just like a bank.
We need a way for the payee to know that the previous owners did not sign any earlier transactions. For our purposes, the earliest transaction is the one that counts, so we don’t care about later attempts to double-spend. The only way to confirm the absence of a transaction is to be aware of all transactions. In the mint based model, the mint was aware of all transactions and decided which arrived first. To accomplish this without a trusted party, transactions must be publicly announced [1], and we need a system for participants to agree on a single history of the order in which they were received. The payee needs proof that at the time of each transaction, the majority of nodes agreed it was the first received.

More...

Is-A 和 Has-A 的取舍

问题

组合和继承是允许在新建的类中放入子对象。其中“Is-A (是一个)”的关系是用继承来表达的,而“Has-A(有一个)”的关系则是用组合来表达的。
组合是显示的这样做的,而继承是隐式的做。组合一般是将现有的类型作为新类型底层实现的一部分来加以复用,在一个类中引用另一个类,而继承是拥有了父类的非私有方法。
二者是之间有何区别?或者怎样在二者之间做出选择呢?

优缺点分析

继承的优点:

  1. 子类可以重写父类的方法来方便地实现对父类的扩展。

继承的缺点:

  1. 父类的内部细节对子类是可见的。
  2. 子类从父类继承的方法在编译时就确定下来了,所以无法在运行期间改变从父类继承的方法的行为。
  3. 如果对父类的方法做了修改的话(比如增加了一个参数),则子类的方法必须做出相应的修改。所以说子类与父类是一种高耦合,违背了面向对象思想。

组合的优点:

  1. 当前对象只能通过所包含的那个对象去调用其方法,所以所包含的对象的内部细节对当前对象时不可见的。
  2. 当前对象与包含的对象是一个低耦合关系,如果修改包含对象的类中代码不需要修改当前对象类的代码。
  3. 当前对象可以在运行时动态的绑定所包含的对象。可以通过set方法给所包含对象赋值。

组合的缺点:

  1. 容易产生过多的对象。
  2. 为了能组合多个对象,必须仔细对接口进行定义。
More...

论文学习 - Bitcoin:A Peer-to-Peer Electronic Cash System(1)

比特币:一个点对点的电子货币系统

Abstract. A purely peer-to-peer version of electronic cash would allow online payments to be sent directly from one party to another without going through a financial institution. Digital signatures provide part of the solution, but the main benefits are lost if a trusted third party is still required to prevent double-spending. We propose a solution to the double-spending problem using a peer-to-peer network. The network timestamps transactions by hashing them into an ongoing chain of hash-based proof-of-work, forming a record that cannot be changed without redoing the proof-of-work. The longest chain not only serves as proof of the sequence of events witnessed, but proof that it came from the largest pool of CPU power. As long as a majority of CPU power is controlled by nodes that are not cooperating to attack the network, they’ll generate the longest chain and outpace attackers. The network itself requires minimal structure. Messages are broadcast on a best effort basis, and nodes can leave and rejoin the network at will, accepting the longest proof-of-work chain as proof of what happened while they were gone.

摘要: 本文提出了一种完全通过点对点技术实现的电子货币系统,它使得在线支付能够直接由一方发起并支付给另外一方,中间不需要通过任何的金融机构。虽然数字签名部分解决了这个问题,但是如果仍然需要可信任的第三方的支持才能防止重复支付的话,那么这种系统的也会失去价值。我们在此提出一种解决方案,使支付系统在点对点的环境下运行,并防止重复支付问题。该网络通过随机散列对全部交易加上时间戳,将它们合并入一个不断延伸的基于随机散列的工作量证明(proof-of-work)的链条作为交易记录,除非重新完成全部的工作量证明,形成的交易记录将不可更改。最长的链条不仅将作为被观察到的事件序列的证明,而且被看做是来自 CPU计算能力最大的池。只要大多数的节点的CPU 计算能力没有被控制用来进行对全网的攻击,那么这些节点将会生成最长的、超过攻击者的链条。这个系统本身需要的基础设施非常少。信息尽最大努力在全网传播即可,节点可以随时离开和重新加入网络,并将接受最长的工作量证明链条作为在该节点离线期间发生的交易的证明。

More...

初识数据埋点(二)

实际案例:

**实例背景:**某汽车互联网公司,领导对负责新车业务的产品经理X君、负责二手车业务的产品经理Y君提出需求:对新车APP和二手车APP销售线索数据指标进行数据监控,如有超过5%的数据变动,则需要向上级汇报波动数值以及波动原因。
名词注释:

  • 销售线索:通过事件记录到用户有明确的购买意向,记录行为的事件例如:电话咨询、短信询价、加入心愿单、收藏、特别关注等类型事件。记录一个用户即代表一个线索。
  • 数据波动:即((当日数据-昨天数据)/昨日数据)*100%=环比数据波动

根据领导需求,假设定义短信砍价按钮与电话咨询按钮为销售线索指标,销售线索按钮页面的入口来源页面包含:页面A与页面B。
X君与Y君分别设计了埋点方案,如图所示:

More...

软件系统解耦:理解依赖关系(二)

单向依赖与单一职责原则(SRP)

单向依赖是最简单的依赖。

上述都是单向依赖的例子。其中,(1)是最理想的情况。当逻辑变复杂后,单个模块往往承担了过多的责任。即使模块之间可以保持简单的单向关系,模块内部各行为之间却形成高强度的耦合整体。根据单一职责原则(SRP),这样的模块也是难以维护的,我们需要对模块做拆分。
在有多个模块的情况下,(2)的依赖关系显然要好于(3),因为在(2)中模块的依赖关系要比(3)少。这样的解释过于抽象,我们用游戏中比较典型的一个应用场景来说明一下。

场景对象管理器GameObjectManager,管理着场景对象GameObjectInstance,而场景对象的构造需要资源AssetStore的支持。他们的调用关系,用(2)和(3)的模式分别实现一遍:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//(2) GameObjectManager从AssetStore取资源数据,然后调用GameObjectInstnce的初始化流程
class GameObjectManager{
public:
AssetForGameObject* GetAsset(DWORD dwID){m_Asset.GetAsset(dwID);}
GameObjectInstance* Create(DWORD dwAssetID){
AssetForGameObject* pAsset = GetAsset(dwAssetID);
return m_GameObjects[dwNewID] = new GameObjectInstance(pAsset);
}
void TickGameObject(){foreach(auto go = m_GameObjects) go.Tick();}
private:
AssetStore m_Asset;
map<DWORD, GameObjectInstance*> m_GameObjects;
};

//(3) GameObjectInstance自己调用AssetStore的方法取资源数据,做初始化
class GameObjectManager{
public:
GameObjectInstance* Create(AssetStore* pAssets, DWORD dwAssetID){
GameObjectInstance* pGo = new GameObjectInstance();
pGo->Init(pAssets, dwAssetID);
return m_GameObjects[dwNewID] = pGo;
}
private:
AssetStore m_Asset;
map<DWORD, GameObjectInstance*> m_GameObjects;
};

class GameObjectInstance{
public:
void Init(AssetStore* pAssets, DWORD dwAssetID){
m_Data = pAssets->GetAsset(dwAssetID);
}
};

GameObjectInstance只需要依赖于AssetForGameObject,但是在依赖关系(3)中,却要依赖于一个范围更大的概念AssetStore。

More...

软件系统解耦:理解依赖关系(一)

在实际工作中,复杂度上来后,各模块之间错综复杂,调用关系网千头万绪。即使有各种设计模式做指导,做出合理的设计也并不容易。程序员天天疲于应对层出不穷的变化,在不断紧逼的deadline压力下,面对巨大的重构工作量往往感到心有余而力不足。
系统复杂度的根源除了业务本身的复杂度,就是设计了不恰当的耦合关系。本文试图探讨依赖关系的主要类型,并总结应对依赖的编程范式。

耦合:依赖和变化

耦合是一个有歧义的词语(为什么“耦合”概念该要摒弃)。当我们说“A和B耦合”的时候,我们是想表达A和B之间有紧密的联系。具体是什么,不容易讲清楚。
在我看来,耦合至少包含了两个方面的含义:依赖和变化。
业务逻辑固有的复杂度决定了,模块之间必然存在着依赖。规范模块间的依赖关系,就是梳理业务复杂度的过程。最终的成果反映在代码中,代表了对业务复杂度的一种认识。这种认识随着业务需求的变化而演化,随着设计者的能力提升而深化。依赖不能被消除,但是可以被优化。探讨一些应对的范式有助于规避已知的陷阱。
变化则来源于两个方面:发展中的用户需求,完善中的系统模型。用户的需求是我们努力的方向。系统模型则代表了我们对需求的理解,是经验和智慧的结晶。一个完善的系统模型,表达能力要足够强,对业务的适应能力要足够强。变化,意味着工作量,意味着成本,应该尽量降低。如果我们把“系统变更”和“业务需求变更”写成函数:
SystemChange = f(ReqirementChange)
我们希望自变量不变的情况下,“系统变更”这个函数值越小越好。特别是“业务需求变更”在当前系统设计假设条件下产生调整的时候,“系统变更”应该局限在很小的范围内。

依赖的种类

在UML类图中,依赖关系被标记为<>。A依赖B意味着,A模块可以调用B模块暴露的API,但B模块绝不允许调用A模块的API(IBM Knowledge Center)。
在类图中,依赖关系是指更改一个类(供应者)可能会导致更改另一个类(客户)。供应者是独立的,这是因为更改使用者并不会影响供应者。
例如,Cart 类依赖于 Product 类,因为 Product 类被用作 Cart 类中的“添加”操作的参数。在类图中,依赖关系是从 Cart 类指向 Product 类。换句话说,Cart 类是使用者元素,而 Product 类是供应者元素。更改 Product 类可能会导致更改 Cart 类。
在类图中,C/C++ 应用程序中的依赖关系将两个类连接起来,以指示这两个类之间存在连接,并且该连接比关联关系更加具有临时性。依赖关系是指使用者类执行下列其中一项操作:

  • 临时使用具有全局作用域的供应者类,
  • 将供应者类临时用作它的某个操作的参数,
  • 将供应者类临时用作它的某个操作的局部变量,
  • 将消息发送至供应者类。

模块之间产生依赖的主要方式是数据引用和函数调用。检验模块依赖程度是否合理,则主要看“变更”的容易程度。软件模块之间的调用方式可以分为三种:同步调用、回调和异步调用(异步消息的传递-回调机制)。同步调用是一种单向依赖关系。回调是一种双向依赖关系。异步调用往往伴随着消息注册操作,所以本质上也是一种双向依赖。

有一种观点将“依赖”直接总结为人脑中的依赖(为什么“耦合”概念该要摒弃)。文中提到:

只要程序员编写模块A时,需要知道模块B的存在,需要知道模块B提供哪些功能,A对B依赖就存在。甚至就算通过所谓的依赖注入、命名查找之类的“解耦”手段,让模块A不需要import B或者include “B.h”,人脑中的依赖仍旧一点都没有变化。唯一的作用是会骗过后文会提到的代码打分工具,让工具误以为两个模块间没有依赖。

代码的复杂度更主要的体现在阅读和理解,如果只是纠结于编译器所看到的依赖,实在是分错了主次,误入了歧途。

初识数据埋点(一)

埋点概述

数据埋点是数据产品经理、数据运营以及数据分析师,基于业务需求(例如:CPC点击付费广告中统计每一个广告位的点击次数),产品需求(例如:推荐系统中推荐商品的曝光次数以及点击的人数)对用户行为的每一个事件对应的位置进行开发埋点,并通过SDK上报埋点的数据结果,记录数据汇总后进行分析,推动产品优化或指导运营。
埋点分析,是网站分析的一种常用的数据采集方法。数据埋点分为初级、中级、高级三种方式。数据埋点主流部署的方式有:

  • 私有化部署(即部署在自己公司的服务器上,如果期望提高数据安全性,或者定制化的埋点方案较多,则适合私有部署,并开发一套针对自己公司定制化的数据后台查询系统保证数据的安全性和精确性,缺点是成本较高)。
  • 接入第三方服务,比如国内的某盟和国外的GA(Google Analytics)统计,在以后的文章中会单独介绍,此处不再展开。(优点是成本较低,部分基础服务免费,缺点是:数据会存在不安全的风险,另外一个就是只能进行通用的简单分析,无法定制化埋点方案)
    此处只展开初级:在产品、服务转化关键点植入统计代码,据其独立ID确保数据采集不重复(如收藏按钮点击率);

主要的埋点事件分类:

点击事件:

点击事件,用户点击按钮即算点击事件,不管点击后有无结果;

曝光事件:

成功打开一次页面记一次,刷新页面一次记一次,加载下一页新页,加载一次记一次。home键切换到后台再进入页面,曝光事件不记;

页面停留时间事件:

表示一个用户在X页面的停留时长记为停留时长。例如:小明9:00访问了X网站首页,此时分析工具则开始为小明这个访问者记录1个Session(会话)。接着9:01小明又浏览了另外一个页面列表页,然后离开了网站(离开网站可以是通过关闭浏览器,或在地址栏键入一个不同的网址,或是点击了你网站上链接到其他网站的链接……)为了简单,我们把这个过程当做一个Session。
则最终小明在首页的页面停留时间:
(Time on Page,简称Tp)Tp(首页) = 9:01 – 9:00 = 1 分钟

More...

37signals的决策原则

背景

37signals 是一间芝加哥互联网应用公司,著名产品如 Basecamp,并以开发 Rails 框架著称。下面是他们公司公布的决策原则

决策原则

以下是我们在37signals做决定时努力牢记的一些一般原则。它们不是要求,这也不是我们在面临选择时的全面清单,但它们可以作为我们在做每天都需要做的一件事时的框架、考虑和共同做法:决定。

  1. 为什么我们要做这个决定,为什么要现在做这个决定? Why are we deciding anything at all? Does a decision actually need to be made here?
  2. 是否是正确的人来做这个决定?谁是拥有正确信息、背景和洞察里的正确人选(不是角色)?谁只是在插嘴? Is the right person making this decision? Not the right role, but the right person with the right information, context, and insight? Who’s merely chiming in?
  3. 如果忽略眼前利益,我们一年以后还会对这个决定有什么感觉? If we remove the immediate impact, how do we think we’ll feel about this decision a year from now?
  4. 为什么还没有做出决定?为什么我们之前没有决定? Why hasn’t this decision been made already? Why didn’t we decide before?
  5. 为什么我们要花这么长时间做这个决定,我们在犹豫什么?这说明什么? What’s taking so long to make this decision? Why are we hesitating? What does that reveal?
More...

请我喝杯咖啡吧~

支付宝
微信