精华内容
下载资源
问答
  • 文华指标公式期货软件波段期货狙击手一套完整的分析交易系统.doc
  • 我个人从2016年就开始在做区块链系统开发的相关工作,最近身边很多朋友都在找我,想让我给他们讲讲区块链技术开发的相关知识,介于此,索性我就手撸了一套简单的java区块链系统,并写了一份详细的开发教程,发布到...


    Tips: 有需要资料或者讨论技术问题的,可以一起交流
    在这里插入图片描述

    文末有本文完整源码链接


    前言

    近几年区块链概念越来越火,特别是区块链技术被纳入国家基础设施建设名单后,各大企业也开始招兵买马,对区块链技术进行研究,从各大招聘网站的区块链职位来看,薪资待遇都很不错,月薪30K到80K的都有,这对于我们程序员来说也是一种机遇,说明学习区块链技术刻不容缓。

    我个人从2016年就开始在做区块链系统开发的相关工作,最近身边很多朋友都在找我,想让我给他们讲讲区块链技术开发的相关知识,介于此,索性我就手撸了一套简单的java区块链系统,并写了一份详细的开发教程,发布到博客,希望能够对热爱区块链技术的小伙伴学习入门有所帮助。

    这套区块链系统代码非常简洁清晰,对于新手来说非常好理解,旨在告诉大家其实区块链技术并没有那么高深复杂。系统中除了springboot框架外,其他基本都是纯原生开发,就连P2P网络也是用的java socket来实现的。

    image.png

    一、区块链技术理论基础

    1、基本概念

    (1)区块链

    从技术层面来看,区块链是由包含交易信息的区块按照时间顺序从后向前有序链接起来的数据结构。

    从应用层面来说,区块链是一个分布式的共享账本和数据库,具有去中心化、不可篡改、全程留痕、集体维护、公开透明等特点。基于这些特点,区块链技术可以开发出自带信任体系特征的系统,实现多个主体之间的协作信任与一致行动。

    区块是区块链中的最小组成单位,主要有包含元数据的区块头和存放一条或者多条交易信息的区块体两部分组成,每个区块都记录着当前区块的哈希和上一个区块的哈希,通过两个哈希值的关联,让所有的区块以链式结构串起来,就形成了一个完整的区块链。

    区块链中的第一个区块被称作为创世区块,无需关联上一个区块。以BTC网络为例,每个区块主要包含如下信息字段:

    • 区块大小:用字节表示的区块数据大小
    • 区块头:组成区块头的包括以下几个字段:
      1. 区块头hash值
      2. 父区块头hash值
      3. 时间戳:区块产生的近似时间
      4. Merkle根:该区块中交易的merkle树根的哈希值
      5. 难度目标:该区块工作量证明算法的难度目标
      6. Nonce:用于工作量证明算法的计数器
    • 交易计数器:交易的数量
    • 交易:记录在区块里的交易信息

    区块链结构的简易模型,如下图所示:
    区块链模型.jpg
    区块中的交易集合记录的是一些特定的信息,在BTC网络中主要记录的是交易信息,在其他区块链网络中可以按照业务逻辑来保存相应的业务数据,如审计信息、版权信息、票据信息等,这也是区块链经常用来当做共享账本的原因。

    打个比方,可以把区块链当做一个用来记账的笔记本,一个区块就相当于一页纸,上面记录了某一时间段內所有的账务信息,从第一页到最后一页,按照页码顺序排列起来就是一个完整的账本。

    (2)区块链网络

    实际的区块链系统由多个区块链节点组成,每个节点都运行着相同一套区块链主干网络的副本,且各个节点间通过P2P网络进行交互,并最终形成一个完整的区块链网络系统。

    P2P网络具有可靠性、去中心化,以及开放性,各个节点之间交互运作、协同处理,每个节点在对外提供服务的同时也使用网络中其他节点所提供的服务。当某一个区块链节点产生新的区块时,会通过广播的方式告诉其他节点,其他节点通过网络接收到该区块信息时,会对这个区块信息进行验证,当有一定数量的节点都验证通过后,各个节点会把该区块更新到各自现有的区块链上,最终使得整个区块链网络中的各个节点信息保持一致,这也是区块链去中心化、可信任特性的体现。

    区块链网络简易模型,如下图所示:
    区块链网络.jpg

    2、区块链分类

    (1)公有链

    公有区块链(Public Block Chains)是指:世界上任何个体或者团体都可以发送交易,且交易能够获得该区块链的有效确认,任何人都可以参与使用和维护该区块链,信息公开透明。公有区块链是最早的区块链,例如BTC、以太坊等虚拟数字货币均基于公有区块链。不过目前公有链实际应用价值不大,并没有产生特别合适的应用场景。

    (2)联盟链

    行业区块链(Consortium Block Chains):由某个群体内部指定多个预选的节点为记账人,每个块的生成由所有的预选节点共同决定(预选节点参与共识过程),其他接入节点可以参与交易,但有权限限制,信息受保护,如银联组织。目前联盟链是各个区块链技术团队主要研究的对象,由于联盟链拥有区块链技术的大部分特征,并且在权限管理、数据安全、监管方面更有优势,是企业优先考虑的区块链技术方案。

    市面上也有一些比较主流的联盟链技术框架,让开发维护联盟链更加便捷。国内一些大的软件厂商也都有自己的企业区块链技术解决方案,例如蚂蚁金服区块链平台,腾讯的TrustSQL平台,东软的SaCa EchoTrust区块链应用平台以及京东区块链防伪追溯平台等等。

    (3)私有链

    私有区块链(Private Block Chains):仅仅使用区块链的总账技术进行记账,可以是一个公司,也可以是个人,独享该区块链的写入权限,利用区块链的不易篡改特性,把区块链作为账本数据库来使用。

    3、关键技术与特性

    (1)共识机制

    共识机制被称作为区块链系统的灵魂,是区块链系统信任体系的基础。区块链系统作为一个多节点的分布式账本系统,当有新的信息需要记录时,哪个节点来负责记账,记账奖励发放给哪个节点,哪些节点负责验证记账结果,如何让各个节点达成最终一致,将记账结果被网络中所有节点以同样的顺序复制并记录下来,就是共识机制要做的事情。

    而按照百度百科上的说法:

    所谓“共识机制”是通过特殊节点的投票,在很短的时间内完成对交易的验证和确认,对一笔交易,如果利益不相干的若干个节点能够达成共识,我们就可以认为全网对此也能够达成共识。再通俗一点来讲,如果中国一名微博大V、美国一名虚拟币玩家、一名非洲留学生和一名欧洲旅行者互不相识,但他们都一致认为你是个好人,那么基本上就可以断定你这人还不坏。

    目前,较为主流的共识算法有PoW、PoS、DPoS、PBFT等,在实际使用时,每种算法都有各自的优点和缺点。在应用于不同场景时,区块链项目将会采用不同的共识机制和算法。

    (2)去中心化

    去中心化,是互联网发展过程中形成的社会关系形态和内容产生形态,是相对于“中心化”而言的新型网络内容生产过程。在一个分布有众多节点的区块链系统中,每个节点都具有高度自治的特征。任何一个节点都可能成为阶段性的中心,但不具备强制性的中心控制功能。节点与节点之间的影响,会通过网络而形成关联关系。这种开放式、扁平化、平等性的系统现象或结构,我们称之为去中心化。

    去中心化的系统具有容错力高、抗攻击力强的特征。中心化的系统一旦中心出现问题,整个系统都会崩溃,但是区块链系统中的任何一个节点出现问题,并不会对整个区块链网络产生太大的影响。

    另外,去中介化并不代表着不接受监管,“去中心化”去的是中央控制方和中介方,而不是监管方。监管节点可以方便地接入任何一个区块链网络。并且由于区块链的公开透明特性,监管机构反而可以更加方便地监控整个系统的交易数据。

    (3)智能合约

    从技术层面讲,智能合约是一段部署在在区块链上的程序代码,当满足程序设定的条件时,它便会在区块链上运行,并得到相应的结果。这种情况有点类似于微信的小程序,区块链提供虚拟机和脚本语言,用户根据脚本语言的语法开发带有一定业务逻辑的程序,部署在区块链上,当满足执行的条件时,智能合约便会被区块链虚拟机解释并运行。

    典型的应用便是以太坊平台的智能合约,在这个平台里可以支持用户通过简单的几行代码就能实现他们想要的合约,实现无需人为监督的、不可篡改、自动化运行的合约,买卖房子不需要再找中介、借钱不需要再找公证人……人们可以随时随地根据自身需求发起合约,它的执行不依赖某个人和组织,所有的信任完全基于以太坊区块链平台本身。

    (4)不可篡改性

    大部分人习惯称它为不可篡改性,但是从技术层面来说,我个人觉得叫做不可逆转性更贴切,既然是一个计算机系统,增删改查是基本的功能属性,只不过区块链系统删除和修改操作比较特殊一点。

    区块链是由每个区块的哈希值串连起来的链式结构,而区块的哈希值=SHA256(“当前区块内容+上一个区块的哈希值”),任何一个区块的内容发生修改,都会引起哈希值的变化,而哈希值的变化也会引起子区块哈希值发生变化,进而引起整个区块链的改变。

    因此任何人想要修改区块的数据几乎是不可能的,除非他把整个区块链中从创世区块到最新的区块的所有哈希值全部重新修改一遍,并且修改完之后,还得广播告诉网络中的其他所有节点,让其他所有节点接受修改。

    不过按照目前计算机的算力,想要在短时间内从区块链头部到尾部全部修改一遍,是一件非常困难的事,并且即使修改完了,其他节点也不会接受修改,因为凭一己之力,没有能够让所有节点达成共识的条件。

    4、流行的区块链框架与应用

    (1)公有链应用:BTC网络

    区块链1.0产品,对于比特币,中本聪是这样定义的:是一种完全通过点对点技术实现的电子现金系统,它使得在线支付能够直接由一方发起并支付给另外一方,中间不需要通过任何的金融机构。

    与所有的货币不同,比特币不依靠特定货币机构发行,它依据特定算法,通过大量的计算产生,比特币经济使用整个P2P网络中众多节点构成的分布式数据库来确认并记录所有的交易行为,并使用密码学的设计来确保货币流通各个环节安全性。之后人们根据比特币网络技术整理出了区块链技术体系,去解决信任的问题,而比特币网络原理也成为了区块链技术初学者的经典教材。

    (2)公有链应用:以太坊网络

    区块链2.0产品的代表,以太坊是一个为去中心化应用(Dapp)而生的开源区块链平台,拥有着大部分区块链技术的特征,但与其它区块链不同的是,以太坊是可编程的,开发者可以用它来构建不同的应用程序,通过其专用加密货币以太币(简称“ETH”)提供去中心化的以太虚拟机(Ethereum Virtual Machine)来处理点对点合约(就是一些脚本程序代码)。如果把比特币网络看作是一套分布式的数据库,而以太坊则更进一步,它可以看作是一台分布式的计算机:区块链是计算机的ROM,合约是程序,而以太坊的矿工们则负责计算,担任CPU的角色。

    image.png
    以太坊的概念首次在2013至2014年间由程序员Vitalik Buterin受比特币启发后提出,大意为“下一代加密货币与去中心化应用平台”。虽然以太坊作为平台可以在其上开发新的应用,但是由于以太坊的运行和BTC网络一样,采用的是Token机制,且平台性能不足,经常出现网络拥堵的情况,平台用来学习开发与测试区块链技术还可以,用于实际生产的话不太现实。

    (3)联盟链开发框架:Hyperledger Fabric

    Hyperledger Fabric 也叫超级账本,它是 IBM 贡献给 Linux 基金会的商用分布式账本,是面向企业应用的全球最大的分布式开源项目。像其他区块链技术一样,它也有一个账本,可以使用智能合约。Fabric的智能合约可以有多种架构,它可以用主流语言编程,例如Go、Java和Javascript,此外也可以使用Solidity。

    至今,Fabric已获得了阿里巴巴、AWS、Azure、百度、谷歌、华为、IBM、甲骨文、腾讯等互联网巨头的支持。许多企业的区块链平台都把Fabric作为底层框架来使用,例如甲骨文。不过由于IBM对区块链的定义强调了区块链的分布式和不可变两个元素,对共识机制进行了削弱,采用了Kafka和zookeeper的“排序服务”实现共识,因此部分业内人士也称超级账本是“伪区块链”,但是即便如此,也抵挡不了企业对超级账本的喜爱,目前Fabric 2.0版本已经正式发布。

    (4)小结

    目前公有链在实际应用中并没有太多的业务场景落地,大部分都是以挖矿为主题或者线上宠物饲养的游戏为主,并且由于数字货币的匿名性,有些不法分子利用这一特点,将数字货币用于洗钱、暗网买卖等违法行为,是各个国家的打击对象,我国政策法规也严厉禁止,因此对于技术人员来说,公有链可以作为研究学习的对象,其他方面暂时没有太多实际意义。

    目前大部分区块链企业的研究方向主要是针对企业的联盟链和私有链,并且国家层面也在大力支持区块链技术的发展,特别是区块链底层核心技术的研发,倡导把区块链作为核心技术自主创新的重要突破口,明确主攻方向,加大投入力度,着力攻克一批关键核心技术,加快推动区块链技术和产业创新发展。不过现在市面上主流的区块链平台大部分还是以国外公司主导的为主,国内区块链底层核心技术的发展,还需要技术人员的加倍努力。

    二、区块链技术Java实现

    1、区块链技术架构

    区块链技术架构图.jpg
    目前主流的区块链技术架构主要分为五层,数据层是最底层的技术,主要实现了数据存储、账户信息、交易信息等模块,数据存储主要基于Merkle树,通过区块的方式和链式结构实现,而账户和交易基于数字签名、哈希函数和非对称加密技术等多种密码学算法和技术,来保证区块链中数据的安全性。

    网络层主要实现网络节点的连接和通讯,又称点对点技术,各个区块链节点通过网络进行通信。共识层是通过共识算法,让网络中的各个节点对全网所有的区块数据真实性正确性达成一致,防止出现拜占庭攻击、51攻击等区块链共识算法攻击。

    激励层主要是实现区块链代币的发行和分配机制,是公有链的范畴,我们不做分析。应用层一般把区块链系统作为一个平台,在平台之上实现一些去中心化的应用程序或者智能合约,平台提供运行这些应用的虚拟机。

    接下来我们基于Java语言来开发一套小型的区块链系统,来实现数据层、网络层、共识层的一些功能,用简单的代码来直观抽象的概念,以便加深对以上区块链技术基础理论的理解。

    2、基于java的区块链开发实战

    (1)开发环境

    开发工具VSCode
    开发语言Java
    JDK版本JDK1.8或者OpenJDK11
    开发框架SpringBoot2.2.1
    工程管理Maven3.6
    测试工具Postman

    (2)区块链基本模型构建

    区块是区块链系统的最小单元,第一步我们先实现最简单的区块结构,新建Block.java类,主要包含以下几个字段:
    Block.java

    /**
     * 区块结构
     * 
     * @author Jared Jia
     *
     */
    public class Block implements Serializable {
    
    	private static final long serialVersionUID = 1L;
    	/**
    	 * 区块索引号(区块高度)
    	 */
    	private int index;
    	/**
    	 * 当前区块的hash值,区块唯一标识
    	 */
    	private String hash;
    	/**
    	 * 前一个区块的hash值
    	 */
    	private String previousHash;
    	/**
    	 * 生成区块的时间戳
    	 */
    	private long timestamp;
    	/**
    	 * 工作量证明,计算正确hash值的次数
    	 */
    	private int nonce;
    	/**
    	 * 当前区块存储的业务数据集合(例如转账交易信息、票据信息、合同信息等)
    	 */
    	private List<Transaction> transactions;
      
      /*** 省略get set方法****/
      }
    

    区块链是由区块按照区块哈希前后顺序串联起来的数据结构,哈希值通过散列算法对区块进行二次哈希计算而得到的数字摘要信息(不了解散列函数的,可以先百度了解一下SHA算法),用于保证区块的信息安全以及整条区块链的有效性。因此第二步我们新增计算区块Hash值的方法,采用SHA256算法,通过java实现:
    CryptoUtil.java

    /**
     * 密码学工具类
     * 
     * @author Jared Jia
     *
     */
    public class CryptoUtil {
    
    	/**
    	 * SHA256散列函数
    	 * @param str
    	 * @return
    	 */
    	public static String SHA256(String str) {
    		MessageDigest messageDigest;
    		String encodeStr = "";
    		try {
    			messageDigest = MessageDigest.getInstance("SHA-256");
    			messageDigest.update(str.getBytes("UTF-8"));
    			encodeStr = byte2Hex(messageDigest.digest());
    		} catch (Exception e) {
    			System.out.println("getSHA256 is error" + e.getMessage());
    		}
    		return encodeStr;
    	}
    	
    	private static String byte2Hex(byte[] bytes) {
    		StringBuilder builder = new StringBuilder();
    		String temp;
    		for (int i = 0; i < bytes.length; i++) {
    			temp = Integer.toHexString(bytes[i] & 0xFF);
    			if (temp.length() == 1) {
    				builder.append("0");
    			}
    			builder.append(temp);
    		}
    		return builder.toString();
    	}
    }
    

    第三步,创建一个链式结构对象,按照先后顺序来保存区块对象,从来形成一个有序的区块链表,考虑到线程安全问题,采用CopyOnWriteArrayList来实现,为了方便测试,暂且把区块链结构保存在本地缓存中,实际的区块链网络最终会实现持久层的功能,把区块链数据保存至数据库中,例如BTC核心网络采用的是K-V数据库LevelDB:
    BlockCache.java

    public class BlockCache {
    
    	/**
    	 * 当前节点的区块链结构
    	 */
    	private List<Block> blockChain = new CopyOnWriteArrayList<Block>();
      
      public List<Block> getBlockChain() {
    		return blockChain;
    	}
    
    	public void setBlockChain(List<Block> blockChain) {
    		this.blockChain = blockChain;
    	}
     }
    

    第四步,有了区块链结构后,需要新增向区块链中添加区块的方法,同时每次添加区块的时候,我们需要验证新区块的有效性,例如Hash值是否正确,新区块中上一区块的Hash属性的值,与上一区块的Hash值是否相等。

    另外,区块链中必须有个创世区块,我们直接通过硬编码实现:
    BlockService.java

    /**
     * 区块链核心服务
     * 
     * @author Jared Jia
     *
     */
    @Service
    public class BlockService {
    
    	@Autowired
    	BlockCache blockCache;
      
    	/**
    	 * 创建创世区块
    	 * @return
    	 */
    	public String createGenesisBlock() {
    		Block genesisBlock = new Block();
    		//设置创世区块高度为1
    		genesisBlock.setIndex(1);
    		genesisBlock.setTimestamp(System.currentTimeMillis());
    		genesisBlock.setNonce(1);
    		//封装业务数据
    		List<Transaction> tsaList = new ArrayList<Transaction>();
    		Transaction tsa = new Transaction();
    		tsa.setId("1");
    		tsa.setBusinessInfo("这是创世区块");
    		tsaList.add(tsa);
    		Transaction tsa2 = new Transaction();
    		tsa2.setId("2");
    		tsa2.setBusinessInfo("区块链高度为:1");
    		tsaList.add(tsa2);		
    		genesisBlock.setTransactions(tsaList);
    		//设置创世区块的hash值
    		genesisBlock.setHash(calculateHash("",tsaList,1));
    		//添加到已打包保存的业务数据集合中
    		blockCache.getPackedTransactions().addAll(tsaList);
    		//添加到区块链中
    		blockCache.getBlockChain().add(genesisBlock);
    		return JSON.toJSONString(genesisBlock);
    	}
    	
    	/**
    	 * 创建新区块
    	 * @param nonce
    	 * @param previousHash
    	 * @param hash
    	 * @param blockTxs
    	 * @return
    	 */
    	public Block createNewBlock(int nonce, String previousHash, String hash, List<Transaction> blockTxs) {
    		Block block = new Block();
    		block.setIndex(blockCache.getBlockChain().size() + 1);
    		//时间戳
    		block.setTimestamp(System.currentTimeMillis());
    		block.setTransactions(blockTxs);
    		//工作量证明,计算正确hash值的次数
    		block.setNonce(nonce);
    		//上一区块的哈希
    		block.setPreviousHash(previousHash);
    		//当前区块的哈希
    		block.setHash(hash);
    		if (addBlock(block)) {
    			return block;
    		}
    		return null;
    	}
    
    	/**
    	 * 添加新区块到当前节点的区块链中
    	 * 
    	 * @param newBlock
    	 */
    	public boolean addBlock(Block newBlock) {
    		//先对新区块的合法性进行校验
    		if (isValidNewBlock(newBlock, blockCache.getLatestBlock())) {
    			blockCache.getBlockChain().add(newBlock);
    			// 新区块的业务数据需要加入到已打包的业务数据集合里去
    			blockCache.getPackedTransactions().addAll(newBlock.getTransactions());
    			return true;
    		}
    		return false;
    	}
    	
    	/**
    	 * 验证新区块是否有效
    	 * 
    	 * @param newBlock
    	 * @param previousBlock
    	 * @return
    	 */
    	public boolean isValidNewBlock(Block newBlock, Block previousBlock) {
    		if (!previousBlock.getHash().equals(newBlock.getPreviousHash())) {
    			System.out.println("新区块的前一个区块hash验证不通过");
    			return false;
    		} else {
    			// 验证新区块hash值的正确性
    			String hash = calculateHash(newBlock.getPreviousHash(), newBlock.getTransactions(), newBlock.getNonce());
    			if (!hash.equals(newBlock.getHash())) {
    				System.out.println("新区块的hash无效: " + hash + " " + newBlock.getHash());
    				return false;
    			}
    			if (!isValidHash(newBlock.getHash())) {
    				return false;
    			}
    		}
    
    		return true;
    	}
     }
    

    以上关键代码实现之后,我们就构建了一个非常简单的区块链模型,包含一个基本的区块模型和一个区块链模型,并且能够生成新的区块并添加到区块链中,接下来我们进行测试。

    第五步,我们编写一个Controller类进行调用:
    BlockController.java

    @Controller
    public class BlockController {
    
    	@Resource
    	BlockService blockService;
    	
    	@Autowired
    	BlockCache blockCache;
    	
    	/**
    	 * 查看当前节点区块链数据
    	 * @return
    	 */
    	@GetMapping("/scan")
    	@ResponseBody
    	public String scanBlock() {
    		return JSON.toJSONString(blockCache.getBlockChain());
    	}
      
      /**
    	 * 创建创世区块
    	 * @return
    	 */
    	@GetMapping("/create")
    	@ResponseBody
    	public String createFirstBlock() {
    		blockService.createGenesisBlock();
    		return JSON.toJSONString(blockCache.getBlockChain());
    	}
     }
    

    第六步,系统测试
    首先系统启动后,先查看区块链中的数据,可以看到当前系统中的区块链为空:
    image.png
    然后我们调用创建创世区块的方法,查看返回结果:
    image.png

    我们把生成的创世区块添加到本地区块链中后,转换成JSON字符串返回,可以看到当前区块链中存储的有一个区块对象,至此我们已经实现了一个简单的区块链。实际的区块链系统模型要复杂的多,需要根据不同的业务场景扩展相应的字段,但是基本特征都是一样的。

    (3)共识机制实现

    在上章节中,我们实现了一个简单的区块链结构,并且能够生成并添加新的区块,但是问题来了,实际的区块链系统是一个多节点、分布式、去中心化的网络,每个节点通过网络交互,实时同步保存着同样的整条区块链数据,那么我们生成的区块,如何才能被其他节点认可,并同步添加到其他所有节点上呢,这个时候我们就需要一套规则,让所有网络节点的参与者达成能够达成共识,接纳并保存新的区块,也就是所谓的“共识机制”。

    理论基础部分已经提到了,共识机制有很多种,各有各的优势与缺点,接下来我们就用java代码来模拟实现我们最为熟知的一种机制:工作量证明(Proof of Work),顾名思义就是对工作量的证明,在基于POW机制构建的区块链网络中,节点通过计算随机哈希散列的数值争夺记账权,求得正确的数值并生成区块的能力是节点算力的具体表现,计算的过程一般被形象地称为“挖矿”。

    简单来说就是,区块链系统设定一套计算规则或者说是一套计算题,在新区块生成前,各个节点都投入到这道题的求解计算中,哪个节点先计算出结果,并得到其它节点的验证和认可,这个节点就会获得新区块的记账权,并获得系统相应的奖励,共识结束。

    典型的PoW共识机制应用就是BTC网络,在BTC网络中,共识计算的目标是找到满足某个特定要求的区块Hash(哈希值)。这个区块哈希值就是工作结果的一个证明,计算工作的目的就是为了寻找到这个证明值,上一章节中,测试时我们已经见过这个Hash值:

    [
        {
            "hash": "25931395e736653212f0258824df4222ae739ec2d5897310258b0857d4d3870c",
            "index": 1,
            "nonce": 1,
            "timestamp": 1580970554734,
            "transactions": [
                {
                    "businessInfo": "这是创世区块",
                    "id": "1"
                }
            ]
        }
    ]
    

    BTC网络PoW使用的Hashcash算法,大致逻辑如下:

    1. 获取某种公开可知的数据data(BTC网络中,指的是区块头);
    2. 添加一个计数器nonce,初始值设置为0;
    3. 计算data与nonce拼接字符串的哈希值;
    4. 检查上一步的哈希值是否满足某个条件,满足则停止计算,不满足则nonce加1,然后重复第3步和第4步,直到满足这个特定的条件为止。

    接下来我们用Java代码实现这个算法,设定满足的特定条件为,Hash值前4位都是0,则计算成功(实际区块链网络中的特定条件要求更高,对计算的运算能力要求也高,并且系统随着计算难度动态调整满足的特定条件,来保证区块生成的速度)。

    第一步,我们新建一个共识机制服务类,添加一个“挖矿”方法,计算成功后,获取记账权,调用添加区块的方法,把区块添加到区块链中:
    PowService.java

    /**
     * 共识机制
     * 采用POW即工作量证明实现共识
     * @author Administrator
     *
     */
    @Service
    public class PowService {
    
    	@Autowired
    	BlockCache blockCache;
    	
    	@Autowired
    	BlockService blockService;
    	
    	/**
    	 * 通过“挖矿”进行工作量证明,实现节点间的共识
    	 * 
    	 * @return
    	 */
    	public Block mine(){
    		
    		// 封装业务数据集合,记录区块产生的节点信息,临时硬编码实现
    		List<Transaction> tsaList = new ArrayList<Transaction>();
    		Transaction tsa1 = new Transaction();
    		tsa1.setId("1");
    		tsa1.setBusinessInfo("这是IP为:"+CommonUtil.getLocalIp()+",端口号为:"+blockCache.getP2pport()+"的节点挖矿生成的区块");
    		tsaList.add(tsa1);
    		Transaction tsa2 = new Transaction();
    		tsa2.setId("2");
    		tsa2.setBusinessInfo("区块链高度为:"+(blockCache.getLatestBlock().getIndex()+1));
    		tsaList.add(tsa2);
    		
    		// 定义每次哈希函数的结果 
    		String newBlockHash = "";
    		int nonce = 0;
    		long start = System.currentTimeMillis();
    		System.out.println("开始挖矿");
    		while (true) {
    			// 计算新区块hash值
    			newBlockHash = blockService.calculateHash(blockCache.getLatestBlock().getHash(), tsaList, nonce);
    			// 校验hash值
    			if (blockService.isValidHash(newBlockHash)) {
    				System.out.println("挖矿完成,正确的hash值:" + newBlockHash);
    				System.out.println("挖矿耗费时间:" + (System.currentTimeMillis() - start) + "ms");
    				break;
    			}
    			System.out.println("第"+(nonce+1)+"次尝试计算的hash值:" + newBlockHash);
    			nonce++;
    		}
    		// 创建新的区块
    		Block block = blockService.createNewBlock(nonce, blockCache.getLatestBlock().getHash(), newBlockHash, tsaList);
    		return block;
    	}
    	
      /**
    	 * 验证hash值是否满足系统条件
    	 * 暂定前4位是0则满足条件
    	 * @param hash
    	 * @return
    	 */
    	public boolean isValidHash(String hash) {
    		//System.out.println("难度系数:"+blockCache.getDifficulty());
    		return hash.startsWith("0000");
    	}
    }
    

    第二步,编写测试共识机制服务的Controller类方法:
    BlockController.java

    /**
    	 * 工作量证明PoW
    	 * 挖矿生成新的区块 
    	 */
    	@GetMapping("/mine")
    	@ResponseBody
    	public String createNewBlock() {
    		powService.mine();
    		return JSON.toJSONString(blockCache.getBlockChain());
    	}
    

    第三步,启动系统,进行测试。
    首先执行http://localhost:8080/create方法,生成创世区块。
    其次调用http://localhost:8080/mine方法进行工作量计算证明,生成新的区块,并添加到本地区块链中:
    image.png

    我们来看一下,系统后台计算的过程,此次计算共花费1048ms计算出满足条件的Hash值,共计算4850次:
    image.png

    至此,我们实现了一个简单的工作量证明机制,并在当前区块链系统节点上运行,完成了正确结果的计算,生成了一个新的区块。

    接下来我们将会开发一个P2P网络,实现多个节点的同时运行,当一个节点挖矿完成后,通过P2P网络广播给其他节点,其他节点验证通过后,会把新产生的区块添加到自己的区块链上,进而保证整个区块链网络所有节点的数据一致性。

    (4)P2P网络开发

    前面我们已经实现了一个基本的区块链系统,并且实现了PoW工作量证明共识机制,通过挖矿计算出正确的结果同时生成一个新的区块添加到区块链中,但是这些都是基于单节点的运行,实际的区块链是有多个节点同时运行的分布式网络系统,所有节点同时计算抢夺记账权,共同维护一条完整的区块链。

    接下来我们基于Java的WebSocket实现一个Peer-to-Peer网络,实现多个节点间的相互通信,通过本章节,我们将要实现以下功能:

      - 创建一个基于java的p2p网络
      - 运行多个节点,且多个节点通过p2p网络自动同步区块信息
      - 一个节点挖矿生成新的区块后,自动广播给其他所有节点
      - 每个节点在接收到其他节点发送的区块内容后,进行验证,验证通过添加到本地区块链上
      - 在自我节点查看整个区块链内容
    

    开发测试本章节的功能,我们最好准备两台电脑或者虚拟机,再或者Docker集群环境也可以,便于多节点的运行测试。如果只有一台电脑也可以,各个节点运行的端口号设置为不相同即可。

    第一步,开发思路整理
    目前我们已经实现了单节点的区块生成,那么我们接下来只需要实现各个节点的消息同步即可。

    • 首先,通过java代码实现p2p网络的server端和client端,每个节点既是服务端也是客户端。

    • 然后,一个节点启动时,会寻找区块链网络上的有效节点,并建立socket连接(BTC网络可以通过使用“DNS”种子方式获取BTC有效节点,DNS种子提供比特币节点的IP地址列表),我们直接把节点列表配置到application.yml文件中。

    • 接着,从连接上的节点获取最新的区块信息,如果当前节点首次运行,则获取整个区块链信息,并替换到本地。

    • 之后,各个节点同时挖矿计算,哪个节点先计算完成,就把生成的新区块全网广播给其他所有节点(实际的区块链网络一直在计算,我们为了便于测试,手动触发一个节点挖矿产生区块,然后全网广播给其他所有节点)。

    • 最后,当一个节点收到广播内容后,对接收到的新区块进行验证,验证通过后添加到本地区块链上,验证的首要条件是新区块的高度必须比本地的区块链高度要高。

    第二步,先实现P2P网络server端
    新建一个P2PServer类,并添加一个初始化server端的方法:
    P2PServer.java

    /**
     * p2p服务端
     * 
     * @author Jared Jia
     *
     */
    @Component
    public class P2PServer {
    
    	@Autowired
    	P2PService p2pService;
    
    	public void initP2PServer(int port) {
    		WebSocketServer socketServer = new WebSocketServer(new InetSocketAddress(port)) {
    
    			/**
    			 * 连接建立后触发
    			 */
    			@Override
    			public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) {
    				p2pService.getSockets().add(webSocket);
    			}
    
    			/**
    			 * 连接关闭后触发
    			 */
    			@Override
    			public void onClose(WebSocket webSocket, int i, String s, boolean b) {
    				p2pService.getSockets().remove(webSocket);
    				System.out.println("connection closed to address:" + webSocket.getRemoteSocketAddress());
    			}
    
    			/**
    			 * 接收到客户端消息时触发
    			 */
    			@Override
    			public void onMessage(WebSocket webSocket, String msg) {
    				//作为服务端,业务逻辑处理
    				p2pService.handleMessage(webSocket, msg, p2pService.getSockets());
    			}
    
    			/**
    			 * 发生错误时触发
    			 */
    			@Override
    			public void onError(WebSocket webSocket, Exception e) {
    				p2pService.getSockets().remove(webSocket);
    				System.out.println("connection failed to address:" + webSocket.getRemoteSocketAddress());
    			}
    
    			@Override
    			public void onStart() {
    
    			}
    
    		};
    		socketServer.start();
    		System.out.println("listening websocket p2p port on: " + port);
    	}
    }
    
    

    第三步,实现P2P网络client端
    P2PClient.java

    /**
     * p2p客户端
     * 
     * @author Jared Jia
     *
     */
    @Component
    public class P2PClient {
    	
    	@Autowired
    	P2PService p2pService;
    
    	public void connectToPeer(String addr) {
    		try {
    			final WebSocketClient socketClient = new WebSocketClient(new URI(addr)) {
    				@Override
    				public void onOpen(ServerHandshake serverHandshake) {
    					//客户端发送请求,查询最新区块
    					p2pService.write(this, p2pService.queryLatestBlockMsg());
    					p2pService.getSockets().add(this);
    				}
    
    				/**
    				 * 接收到消息时触发
    				 * @param msg
    				 */
    				@Override
    				public void onMessage(String msg) {
    					p2pService.handleMessage(this, msg, p2pService.getSockets());
    				}
    
    				@Override
    				public void onClose(int i, String msg, boolean b) {
    					p2pService.getSockets().remove(this);
    					System.out.println("connection closed");
    				}
    
    				@Override
    				public void onError(Exception e) {
    					p2pService.getSockets().remove(this);
    					System.out.println("connection failed");
    				}
    			};
    			socketClient.connect();
    		} catch (URISyntaxException e) {
    			System.out.println("p2p connect is error:" + e.getMessage());
    		}
    	}
    }
    

    第四步,定义P2P网络同步的消息模型
    同步的消息模型,我们定义为四类,分别是:
    BlockConstant.java

      // 查询最新的区块
    	public final static int QUERY_LATEST_BLOCK = 1;
    
    	// 返回最新的区块
    	public final static int RESPONSE_LATEST_BLOCK = 2;
    
    	// 查询整个区块链
    	public final static int QUERY_BLOCKCHAIN = 3;
    
    	// 返回整个区块链
    	public final static int RESPONSE_BLOCKCHAIN = 4;
    

    定义一个各个节点间传递的消息模型:
    Message.java

    /**
     * p2p通讯消息
     *
     * @author Jared Jia
     * 
     */
    public class Message implements Serializable {
    	
    		private static final long serialVersionUID = 1L;
        /**
         * 消息类型
         */
        private int type;
        /**
         * 消息内容
         */
        private String data;
        
        /****set get方法省略****/
        
      }
    

    **
    第五步,实现向其他节点广播的方法

    新建一个p2p网络服务类,向外发送消息,或者处理当前节点收到其他节点发送的请求。
    P2PService.java

    /**
    	 * 全网广播消息
    	 * @param message
    	 */
    	public void broatcast(String message) {
    		List<WebSocket> socketsList = this.getSockets();
    		if (CollectionUtils.isEmpty(socketsList)) {
    			return;
    		}
    		System.out.println("======全网广播消息开始:");
    		for (WebSocket socket : socketsList) {
    			this.write(socket, message);
    		}
    		System.out.println("======全网广播消息结束");
    	}
    

    第六步,开发消息处理路由

    第五步中已经实现了向外发送消息,本步骤实现接收消息。
    首先设计一个服务端和客户端共用的消息路由,来分发消息给对应的处理单元。
    P2PService.java

    /**
    	 * 客户端和服务端共用的消息处理方法
    	 * @param webSocket
    	 * @param msg
    	 * @param sockets
    	 */
    	public void handleMessage(WebSocket webSocket, String msg, List<WebSocket> sockets) {
    		try {
    			Message message = JSON.parseObject(msg, Message.class);
    			System.out.println("接收到IP地址为:" +webSocket.getRemoteSocketAddress().getAddress().toString()
    					+",端口号为:"+ webSocket.getRemoteSocketAddress().getPort() + "的p2p消息:"
    			        + JSON.toJSONString(message));
    			switch (message.getType()) {
    			//客户端请求查询最新的区块:1
    			case BlockConstant.QUERY_LATEST_BLOCK:
    				write(webSocket, responseLatestBlockMsg());//服务端调用方法返回最新区块:2
    				break;
    			//接收到服务端返回的最新区块:2
    			case BlockConstant.RESPONSE_LATEST_BLOCK:
    				handleBlockResponse(message.getData(), sockets);
    				break;
    			//客户端请求查询整个区块链:3
    			case BlockConstant.QUERY_BLOCKCHAIN:
    				write(webSocket, responseBlockChainMsg());//服务端调用方法返回最新区块:4
    				break;
    			//直接接收到其他节点发送的整条区块链信息:4
    			case BlockConstant.RESPONSE_BLOCKCHAIN:
    				handleBlockChainResponse(message.getData(), sockets);
    				break;
    			}
    		} catch (Exception e) {
    			System.out.println("处理IP地址为:" +webSocket.getRemoteSocketAddress().getAddress().toString()
    				+",端口号为:"+ webSocket.getRemoteSocketAddress().getPort() + "的p2p消息错误:" 
    				+ e.getMessage());
    		}
    	}
    

    第七步,开发消息处理单元

    有了消息路由之后,接着编写不同的处理单元,处理其他节点发送来的区块或者区块链信息,总体原则是:先校验其他节点发送来的区块或者区块链的有效性,然后判断它们的高度比当前节点的区块链高度要高,如果高则替换本地的区块链,或者把新区块添加到本地区块链上。
    P2PService.java

    /**
    	 * 处理其它节点发送过来的区块信息
    	 * @param blockData
    	 * @param sockets
    	 */
    	public synchronized void handleBlockResponse(String blockData, List<WebSocket> sockets) {
    		//反序列化得到其它节点的最新区块信息
    		Block latestBlockReceived = JSON.parseObject(blockData, Block.class);
    		//当前节点的最新区块
    		Block latestBlock = blockCache.getLatestBlock();
    		
    		if (latestBlockReceived != null) {
    			if(latestBlock != null) {
    				//如果接收到的区块高度比本地区块高度大的多
    				if(latestBlockReceived.getIndex() > latestBlock.getIndex() + 1) {
    					broatcast(queryBlockChainMsg());
    					System.out.println("重新查询所有节点上的整条区块链");
    				}else if (latestBlockReceived.getIndex() > latestBlock.getIndex() && 
    						latestBlock.getHash().equals(latestBlockReceived.getPreviousHash())) {
    					if (blockService.addBlock(latestBlockReceived)) {
    						broatcast(responseLatestBlockMsg());
    					}
    					System.out.println("将新接收到的区块加入到本地的区块链");
    				}
    			}else if(latestBlock == null) {
    				broatcast(queryBlockChainMsg());
    				System.out.println("重新查询所有节点上的整条区块链");
    			}
    		}
    	}
    	
    	/**
    	 * 处理其它节点发送过来的区块链信息
    	 * @param blockData
    	 * @param sockets
    	 */
    	public synchronized void handleBlockChainResponse(String blockData, List<WebSocket> sockets) {
    		//反序列化得到其它节点的整条区块链信息
    		List<Block> receiveBlockchain = JSON.parseArray(blockData, Block.class);
    		if(!CollectionUtils.isEmpty(receiveBlockchain) && blockService.isValidChain(receiveBlockchain)) {
    			//根据区块索引先对区块进行排序
    			Collections.sort(receiveBlockchain, new Comparator<Block>() {
    				public int compare(Block block1, Block block2) {
    					return block1.getIndex() - block2.getIndex();
    				}
    			});
    			
    			//其它节点的最新区块
    			Block latestBlockReceived = receiveBlockchain.get(receiveBlockchain.size() - 1);
    			//当前节点的最新区块
    			Block latestBlock = blockCache.getLatestBlock();
    			
    			if(latestBlock == null) {
    				//替换本地的区块链
    				blockService.replaceChain(receiveBlockchain);
    			}else {
    				//其它节点区块链如果比当前节点的长,则处理当前节点的区块链
    				if (latestBlockReceived.getIndex() > latestBlock.getIndex()) {
    					if (latestBlock.getHash().equals(latestBlockReceived.getPreviousHash())) {
    						if (blockService.addBlock(latestBlockReceived)) {
    							broatcast(responseLatestBlockMsg());
    						}
    						System.out.println("将新接收到的区块加入到本地的区块链");
    					} else {
    						// 用长链替换本地的短链
    						blockService.replaceChain(receiveBlockchain);
    					}
    				}
    			}
    		}
    	}
    

    3、完整系统运行与测试

    第一步,打包生成测试用的可执行jar包
    准备两台机器(虚拟机或者Docker集群都行),同时运行两个节点,节点信息如下:

    Node1Node2
    IP地址192.168.0.104192.168.0.112
    http端口号80808090
    websocket服务端口号70017002
    websocket服务地址ws://192.168.0.104:7001ws://192.168.0.112:7002

    通过mvn package -Dmaven.test.skip=true命令对工程进行打包,生成可直接运行的jar包,打包前对工程进行配置,配置信息如下图:
    节点Node1打包:
    image.png

    节点Node2打包:
    image.png

    分别打包之后,生成两个节点的可执行jar包,如下:
    image.png

    把两个jar包分别放在对应IP的windows机器上,打开命令行模式,进入jar所在文件夹,分别执行以下命令运行两个节点:
    java -jar dce-blockchain-node1.jar
    java -jar dce-blockchain-node2.jar

    启动节点2的时候,可以看到后台日志,已经连接上节点1,如下图所示:
    image.png

    第二步,对两个节点进行测试

    首先,两个节点启动后,用postman分别执行http://192.168.0.104:8080/scanhttp://192.168.0.112:8090/scan请求,可以看到两个节点的区块链内容都为空。

    然后,在节点1上分别执行http://192.168.0.104:8080/createhttp://192.168.0.104:8080/mine请求,来生成创世区块,以及通过挖矿产生第二个区块,执行后查看节点1的区块链信息如下:
    执行http://192.168.0.104:8080/scan结果:

    [
        {
            "hash": "5303d2990c139992bdb5a22aa1dac4f2719755304e45bac03ca4a1f1688c909e",
            "index": 1,
            "nonce": 1,
            "timestamp": 1581064647736,
            "transactions": [
                {
                    "businessInfo": "这是创世区块",
                    "id": "1"
                },
                {
                    "businessInfo": "区块链高度为:1",
                    "id": "2"
                }
            ]
        },
        {
            "hash": "0000de5eea0c20c2e7d06220bc023886e88dd8784eaa2fd2d1d6c5e581061d85",
            "index": 2,
            "nonce": 4850,
            "previousHash": "5303d2990c139992bdb5a22aa1dac4f2719755304e45bac03ca4a1f1688c909e",
            "timestamp": 1581064655139,
            "transactions": [
                {
                    "businessInfo": "这是IP为:192.168.0.104,端口号为:7001的节点挖矿生成的区块",
                    "id": "1"
                },
                {
                    "businessInfo": "区块链高度为:2",
                    "id": "2"
                }
            ]
        }
    ]
    

    最后,我们来验证节点2是否已经完成了节点1生成的区块链信息的网络同步,Postman执行http://192.168.0.112:8090/scan请求,查看返回结果:
    image.png 从结果可以看到,区块链网络节点2已经接收到节点1发送的区块链信息,系统日志如下:
    image.png

    反过来,我们在节点2上再执行一次挖矿操作,可以看到节点1上,已经接收到节点2挖矿新产生的区块信息,并添加到节点1的区块链上:
    image.png

    至此,我们已经实现了一个完整的小型区块链网络,并实现各个节点间的通信,多个节点共同维护同一个区块链信息。

    结语:
    区块链系统非常庞大,涉及方方面面的技术,本人所演示的代码主要对区块链基础的一些概念进行了诠释,感兴趣的同学,还可以在此基础上继续开发,来实现例如持久层、消息的加密解密、系统账户模型、预言机、侧链技术以及智能合约等区块链系统功能。


    写给每个区块链技术人员:
    目前市面上流行的企业级区块链框架,例如超级账本Fabric都是国外人员在主导,而我们国内除了几家大厂外,其他很多区块链公司基本都是把人家的东西拿过来进行二次封装,然后对外声称自己公司已经掌握了区块链核心技术,并对企业提供服务,这是一种不好的现象。大家可以想想我们现在用的开发语言、框架有几个真正是国产的,我们再联想一下前段时间中兴、华为被人家核心技术卡脖子事件,就知道我们要做的事情有很多,我们需要去除浮躁,静下心来好好研究底层核心技术,这样才能实现真正的“弯道超车”!

    三、源代码

    1、本文完整源代码地址

    https://gitee.com/luckytuan/dce-blockchain

    2、参考书籍

    《精通比特币》
    《2018年中国区块链产业白皮书》
    《超级账本Hyperledger白皮书》

    展开全文
  • 基于Java开发一套完整的区块链系统(附源码)

    千次阅读 多人点赞 2021-01-15 16:30:00
    (2)区块链网络 实际的区块链系统由多个区块链节点组成,每个节点都运行着相同一套区块链主干网络的副本,且各个节点间通过P2P网络进行交互,并最终形成一个完整的区块链网络系统。 P2P网络具有可靠性、去中心化,...

    来源:https://blog.csdn.net/victory_long



    前言

    近几年区块链概念越来越火,特别是区块链技术被纳入国家基础设施建设名单后,各大企业也开始招兵买马,对区块链技术进行研究,从各大招聘网站的区块链职位来看,薪资待遇都很不错,月薪30K到80K的都有,这对于我们程序员来说也是一种机遇,说明学习区块链技术刻不容缓。

    我个人从2016年就开始在做区块链系统开发的相关工作,最近身边很多朋友都在找我,想让我给他们讲讲区块链技术开发的相关知识,介于此,索性我就手撸了一套简单的java区块链系统,并写了一份详细的开发教程,发布到博客,希望能够对热爱区块链技术的小伙伴学习入门有所帮助。

    这套区块链系统代码非常简洁清晰,对于新手来说非常好理解,旨在告诉大家其实区块链技术并没有那么高深复杂。系统中除了springboot框架外,其他基本都是纯原生开发,就连P2P网络也是用的java socket来实现的。

    文末有本文完整源码链接。

    一、区块链技术理论基础

    1、基本概念

    (1)区块链

    从技术层面来看,区块链是由包含交易信息的区块按照时间顺序从后向前有序链接起来的数据结构。

    从应用层面来说,区块链是一个分布式的共享账本和数据库,具有去中心化、不可篡改、全程留痕、集体维护、公开透明等特点。基于这些特点,区块链技术可以开发出自带信任体系特征的系统,实现多个主体之间的协作信任与一致行动。

    区块是区块链中的最小组成单位,主要有包含元数据的区块头和存放一条或者多条交易信息的区块体两部分组成,每个区块都记录着当前区块的哈希和上一个区块的哈希,通过两个哈希值的关联,让所有的区块以链式结构串起来,就形成了一个完整的区块链。

    区块链中的第一个区块被称作为创世区块,无需关联上一个区块。以BTC网络为例,每个区块主要包含如下信息字段:

    • 区块大小:用字节表示的区块数据大小

    • 区块头:组成区块头的包括以下几个字段:
      1. 区块头hash值
      2. 父区块头hash值
      3. 时间戳:区块产生的近似时间
      4. Merkle根:该区块中交易的merkle树根的哈希值
      5. 难度目标:该区块工作量证明算法的难度目标
      6. Nonce:用于工作量证明算法的计数器

    • 交易计数器:交易的数量

    • 交易:记录在区块里的交易信息

    区块链结构的简易模型,如下图所示:

    区块中的交易集合记录的是一些特定的信息,在BTC网络中主要记录的是交易信息,在其他区块链网络中可以按照业务逻辑来保存相应的业务数据,如审计信息、版权信息、票据信息等,这也是区块链经常用来当做共享账本的原因。

    打个比方,可以把区块链当做一个用来记账的笔记本,一个区块就相当于一页纸,上面记录了某一时间段內所有的账务信息,从第一页到最后一页,按照页码顺序排列起来就是一个完整的账本。

    (2)区块链网络

    实际的区块链系统由多个区块链节点组成,每个节点都运行着相同一套区块链主干网络的副本,且各个节点间通过P2P网络进行交互,并最终形成一个完整的区块链网络系统。

    P2P网络具有可靠性、去中心化,以及开放性,各个节点之间交互运作、协同处理,每个节点在对外提供服务的同时也使用网络中其他节点所提供的服务。当某一个区块链节点产生新的区块时,会通过广播的方式告诉其他节点,其他节点通过网络接收到该区块信息时,会对这个区块信息进行验证,当有一定数量的节点都验证通过后,各个节点会把该区块更新到各自现有的区块链上,最终使得整个区块链网络中的各个节点信息保持一致,这也是区块链去中心化、可信任特性的体现。

    区块链网络简易模型,如下图所示:

    2、区块链分类

    (1)公有链

    公有区块链(Public Block Chains)是指:世界上任何个体或者团体都可以发送交易,且交易能够获得该区块链的有效确认,任何人都可以参与使用和维护该区块链,信息公开透明。公有区块链是最早的区块链,例如BTC、以太坊等虚拟数字货币均基于公有区块链。不过目前公有链实际应用价值不大,并没有产生特别合适的应用场景。

    (2)联盟链

    行业区块链(Consortium Block Chains):由某个群体内部指定多个预选的节点为记账人,每个块的生成由所有的预选节点共同决定(预选节点参与共识过程),其他接入节点可以参与交易,但有权限限制,信息受保护,如银联组织。目前联盟链是各个区块链技术团队主要研究的对象,由于联盟链拥有区块链技术的大部分特征,并且在权限管理、数据安全、监管方面更有优势,是企业优先考虑的区块链技术方案。

    市面上也有一些比较主流的联盟链技术框架,让开发维护联盟链更加便捷。国内一些大的软件厂商也都有自己的企业区块链技术解决方案,例如蚂蚁金服区块链平台,腾讯的TrustSQL平台,东软的SaCa EchoTrust区块链应用平台以及京东区块链防伪追溯平台等等。

    (3)私有链

    私有区块链(Private Block Chains):仅仅使用区块链的总账技术进行记账,可以是一个公司,也可以是个人,独享该区块链的写入权限,利用区块链的不易篡改特性,把区块链作为账本数据库来使用。

    3、关键技术与特性

    (1)共识机制

    共识机制被称作为区块链系统的灵魂,是区块链系统信任体系的基础。区块链系统作为一个多节点的分布式账本系统,当有新的信息需要记录时,哪个节点来负责记账,记账奖励发放给哪个节点,哪些节点负责验证记账结果,如何让各个节点达成最终一致,将记账结果被网络中所有节点以同样的顺序复制并记录下来,就是共识机制要做的事情。

    而按照百度百科上的说法:

    所谓“共识机制”是通过特殊节点的投票,在很短的时间内完成对交易的验证和确认,对一笔交易,如果利益不相干的若干个节点能够达成共识,我们就可以认为全网对此也能够达成共识。再通俗一点来讲,如果中国一名微博大V、美国一名虚拟币玩家、一名非洲留学生和一名欧洲旅行者互不相识,但他们都一致认为你是个好人,那么基本上就可以断定你这人还不坏。

    目前,较为主流的共识算法有PoW、PoS、DPoS、PBFT等,在实际使用时,每种算法都有各自的优点和缺点。在应用于不同场景时,区块链项目将会采用不同的共识机制和算法。

    (2)去中心化

    去中心化,是互联网发展过程中形成的社会关系形态和内容产生形态,是相对于“中心化”而言的新型网络内容生产过程。在一个分布有众多节点的区块链系统中,每个节点都具有高度自治的特征。任何一个节点都可能成为阶段性的中心,但不具备强制性的中心控制功能。节点与节点之间的影响,会通过网络而形成关联关系。这种开放式、扁平化、平等性的系统现象或结构,我们称之为去中心化。

    去中心化的系统具有容错力高、抗攻击力强的特征。中心化的系统一旦中心出现问题,整个系统都会崩溃,但是区块链系统中的任何一个节点出现问题,并不会对整个区块链网络产生太大的影响。

    另外,去中介化并不代表着不接受监管,“去中心化”去的是中央控制方和中介方,而不是监管方。监管节点可以方便地接入任何一个区块链网络。并且由于区块链的公开透明特性,监管机构反而可以更加方便地监控整个系统的交易数据。

    (3)智能合约

    从技术层面讲,智能合约是一段部署在在区块链上的程序代码,当满足程序设定的条件时,它便会在区块链上运行,并得到相应的结果。这种情况有点类似于微信的小程序,区块链提供虚拟机和脚本语言,用户根据脚本语言的语法开发带有一定业务逻辑的程序,部署在区块链上,当满足执行的条件时,智能合约便会被区块链虚拟机解释并运行。

    典型的应用便是以太坊平台的智能合约,在这个平台里可以支持用户通过简单的几行代码就能实现他们想要的合约,实现无需人为监督的、不可篡改、自动化运行的合约,买卖房子不需要再找中介、借钱不需要再找公证人……人们可以随时随地根据自身需求发起合约,它的执行不依赖某个人和组织,所有的信任完全基于以太坊区块链平台本身。

    (4)不可篡改性

    大部分人习惯称它为不可篡改性,但是从技术层面来说,我个人觉得叫做不可逆转性更贴切,既然是一个计算机系统,增删改查是基本的功能属性,只不过区块链系统删除和修改操作比较特殊一点。

    区块链是由每个区块的哈希值串连起来的链式结构,而区块的哈希值=SHA256(“当前区块内容+上一个区块的哈希值”),任何一个区块的内容发生修改,都会引起哈希值的变化,而哈希值的变化也会引起子区块哈希值发生变化,进而引起整个区块链的改变。

    因此任何人想要修改区块的数据几乎是不可能的,除非他把整个区块链中从创世区块到最新的区块的所有哈希值全部重新修改一遍,并且修改完之后,还得广播告诉网络中的其他所有节点,让其他所有节点接受修改。

    不过按照目前计算机的算力,想要在短时间内从区块链头部到尾部全部修改一遍,是一件非常困难的事,并且即使修改完了,其他节点也不会接受修改,因为凭一己之力,没有能够让所有节点达成共识的条件。

    4、流行的区块链框架与应用

    (1)公有链应用:BTC网络

    区块链1.0产品,对于比特币,中本聪是这样定义的:是一种完全通过点对点技术实现的电子现金系统,它使得在线支付能够直接由一方发起并支付给另外一方,中间不需要通过任何的金融机构。

    与所有的货币不同,比特币不依靠特定货币机构发行,它依据特定算法,通过大量的计算产生,比特币经济使用整个P2P网络中众多节点构成的分布式数据库来确认并记录所有的交易行为,并使用密码学的设计来确保货币流通各个环节安全性。之后人们根据比特币网络技术整理出了区块链技术体系,去解决信任的问题,而比特币网络原理也成为了区块链技术初学者的经典教材。

    (2)公有链应用:以太坊网络

    区块链2.0产品的代表,以太坊是一个为去中心化应用(Dapp)而生的开源区块链平台,拥有着大部分区块链技术的特征,但与其它区块链不同的是,以太坊是可编程的,开发者可以用它来构建不同的应用程序,通过其专用加密货币以太币(简称“ETH”)提供去中心化的以太虚拟机(Ethereum Virtual Machine)来处理点对点合约(就是一些脚本程序代码)。如果把比特币网络看作是一套分布式的数据库,而以太坊则更进一步,它可以看作是一台分布式的计算机:区块链是计算机的ROM,合约是程序,而以太坊的矿工们则负责计算,担任CPU的角色。


    以太坊的概念首次在2013至2014年间由程序员Vitalik Buterin受比特币启发后提出,大意为“下一代加密货币与去中心化应用平台”。虽然以太坊作为平台可以在其上开发新的应用,但是由于以太坊的运行和BTC网络一样,采用的是Token机制,且平台性能不足,经常出现网络拥堵的情况,平台用来学习开发与测试区块链技术还可以,用于实际生产的话不太现实。

    (3)联盟链开发框架:Hyperledger Fabric

    Hyperledger Fabric 也叫超级账本,它是 IBM 贡献给 Linux 基金会的商用分布式账本,是面向企业应用的全球最大的分布式开源项目。像其他区块链技术一样,它也有一个账本,可以使用智能合约。Fabric的智能合约可以有多种架构,它可以用主流语言编程,例如Go、Java和Javascript,此外也可以使用Solidity。

    至今,Fabric已获得了阿里巴巴、AWS、Azure、百度、谷歌、华为、IBM、甲骨文、腾讯等互联网巨头的支持。许多企业的区块链平台都把Fabric作为底层框架来使用,例如甲骨文。不过由于IBM对区块链的定义强调了区块链的分布式和不可变两个元素,对共识机制进行了削弱,采用了Kafka和zookeeper的“排序服务”实现共识,因此部分业内人士也称超级账本是“伪区块链”,但是即便如此,也抵挡不了企业对超级账本的喜爱,目前Fabric 2.0版本已经正式发布。

    (4)小结

    目前公有链在实际应用中并没有太多的业务场景落地,大部分都是以挖矿为主题或者线上宠物饲养的游戏为主,并且由于数字货币的匿名性,有些不法分子利用这一特点,将数字货币用于洗钱、暗网买卖等违法行为,是各个国家的打击对象,我国政策法规也严厉禁止,因此对于技术人员来说,公有链可以作为研究学习的对象,其他方面暂时没有太多实际意义。

    目前大部分区块链企业的研究方向主要是针对企业的联盟链和私有链,并且国家层面也在大力支持区块链技术的发展,特别是区块链底层核心技术的研发,倡导把区块链作为核心技术自主创新的重要突破口,明确主攻方向,加大投入力度,着力攻克一批关键核心技术,加快推动区块链技术和产业创新发展。不过现在市面上主流的区块链平台大部分还是以国外公司主导的为主,国内区块链底层核心技术的发展,还需要技术人员的加倍努力。

    二、区块链技术Java实现

    1、区块链技术架构


    目前主流的区块链技术架构主要分为五层,数据层是最底层的技术,主要实现了数据存储、账户信息、交易信息等模块,数据存储主要基于Merkle树,通过区块的方式和链式结构实现,而账户和交易基于数字签名、哈希函数和非对称加密技术等多种密码学算法和技术,来保证区块链中数据的安全性。

    网络层主要实现网络节点的连接和通讯,又称点对点技术,各个区块链节点通过网络进行通信。共识层是通过共识算法,让网络中的各个节点对全网所有的区块数据真实性正确性达成一致,防止出现拜占庭攻击、51攻击等区块链共识算法攻击。

    激励层主要是实现区块链代币的发行和分配机制,是公有链的范畴,我们不做分析。应用层一般把区块链系统作为一个平台,在平台之上实现一些去中心化的应用程序或者智能合约,平台提供运行这些应用的虚拟机。

    接下来我们基于Java语言来开发一套小型的区块链系统,来实现数据层、网络层、共识层的一些功能,用简单的代码来直观抽象的概念,以便加深对以上区块链技术基础理论的理解。

    2、基于java的区块链开发实战

    (1)开发环境

    开发工具VSCode
    开发语言Java
    JDK版本JDK1.8或者OpenJDK11
    开发框架SpringBoot2.2.1
    工程管理Maven3.6
    测试工具Postman

    (2)区块链基本模型构建

    区块是区块链系统的最小单元,第一步我们先实现最简单的区块结构,新建Block.java类,主要包含以下几个字段:
    Block.java

    /**
     * 区块结构
     * 
     * @author Jared Jia
     *
     */
    public class Block implements Serializable {
    
    
      private static final long serialVersionUID = 1L;
      /**
       * 区块索引号(区块高度)
       */
      private int index;
      /**
       * 当前区块的hash值,区块唯一标识
       */
      private String hash;
      /**
       * 前一个区块的hash值
       */
      private String previousHash;
      /**
       * 生成区块的时间戳
       */
      private long timestamp;
      /**
       * 工作量证明,计算正确hash值的次数
       */
      private int nonce;
      /**
       * 当前区块存储的业务数据集合(例如转账交易信息、票据信息、合同信息等)
       */
      private List<Transaction> transactions;
      
      /*** 省略get set方法****/
      }
    

    区块链是由区块按照区块哈希前后顺序串联起来的数据结构,哈希值通过散列算法对区块进行二次哈希计算而得到的数字摘要信息(不了解散列函数的,可以先百度了解一下SHA算法),用于保证区块的信息安全以及整条区块链的有效性。因此第二步我们新增计算区块Hash值的方法,采用SHA256算法,通过java实现:
    CryptoUtil.java

    /**
     * 密码学工具类
     * 
     * @author Jared Jia
     *
     */
    public class CryptoUtil {
    
    
      /**
       * SHA256散列函数
       * @param str
       * @return
       */
      public static String SHA256(String str) {
        MessageDigest messageDigest;
        String encodeStr = "";
        try {
          messageDigest = MessageDigest.getInstance("SHA-256");
          messageDigest.update(str.getBytes("UTF-8"));
          encodeStr = byte2Hex(messageDigest.digest());
        } catch (Exception e) {
          System.out.println("getSHA256 is error" + e.getMessage());
        }
        return encodeStr;
      }
      
      private static String byte2Hex(byte[] bytes) {
        StringBuilder builder = new StringBuilder();
        String temp;
        for (int i = 0; i < bytes.length; i++) {
          temp = Integer.toHexString(bytes[i] & 0xFF);
          if (temp.length() == 1) {
            builder.append("0");
          }
          builder.append(temp);
        }
        return builder.toString();
      }
    }
    

    第三步,创建一个链式结构对象,按照先后顺序来保存区块对象,从来形成一个有序的区块链表,考虑到线程安全问题,采用CopyOnWriteArrayList来实现,为了方便测试,暂且把区块链结构保存在本地缓存中,实际的区块链网络最终会实现持久层的功能,把区块链数据保存至数据库中,例如BTC核心网络采用的是K-V数据库LevelDB:
    BlockCache.java

    public class BlockCache {
    
    
      /**
       * 当前节点的区块链结构
       */
      private List<Block> blockChain = new CopyOnWriteArrayList<Block>();
      
      public List<Block> getBlockChain() {
        return blockChain;
      }
    
    
      public void setBlockChain(List<Block> blockChain) {
        this.blockChain = blockChain;
      }
     }
    

    第四步,有了区块链结构后,需要新增向区块链中添加区块的方法,同时每次添加区块的时候,我们需要验证新区块的有效性,例如Hash值是否正确,新区块中上一区块的Hash属性的值,与上一区块的Hash值是否相等。

    另外,区块链中必须有个创世区块,我们直接通过硬编码实现:
    BlockService.java

    /**
     * 区块链核心服务
     * 
     * @author Jared Jia
     *
     */
    @Service
    public class BlockService {
    
    
      @Autowired
      BlockCache blockCache;
      
      /**
       * 创建创世区块
       * @return
       */
      public String createGenesisBlock() {
        Block genesisBlock = new Block();
        //设置创世区块高度为1
        genesisBlock.setIndex(1);
        genesisBlock.setTimestamp(System.currentTimeMillis());
        genesisBlock.setNonce(1);
        //封装业务数据
        List<Transaction> tsaList = new ArrayList<Transaction>();
        Transaction tsa = new Transaction();
        tsa.setId("1");
        tsa.setBusinessInfo("这是创世区块");
        tsaList.add(tsa);
        Transaction tsa2 = new Transaction();
        tsa2.setId("2");
        tsa2.setBusinessInfo("区块链高度为:1");
        tsaList.add(tsa2);    
        genesisBlock.setTransactions(tsaList);
        //设置创世区块的hash值
        genesisBlock.setHash(calculateHash("",tsaList,1));
        //添加到已打包保存的业务数据集合中
        blockCache.getPackedTransactions().addAll(tsaList);
        //添加到区块链中
        blockCache.getBlockChain().add(genesisBlock);
        return JSON.toJSONString(genesisBlock);
      }
      
      /**
       * 创建新区块
       * @param nonce
       * @param previousHash
       * @param hash
       * @param blockTxs
       * @return
       */
      public Block createNewBlock(int nonce, String previousHash, String hash, List<Transaction> blockTxs) {
        Block block = new Block();
        block.setIndex(blockCache.getBlockChain().size() + 1);
        //时间戳
        block.setTimestamp(System.currentTimeMillis());
        block.setTransactions(blockTxs);
        //工作量证明,计算正确hash值的次数
        block.setNonce(nonce);
        //上一区块的哈希
        block.setPreviousHash(previousHash);
        //当前区块的哈希
        block.setHash(hash);
        if (addBlock(block)) {
          return block;
        }
        return null;
      }
    
    
      /**
       * 添加新区块到当前节点的区块链中
       * 
       * @param newBlock
       */
      public boolean addBlock(Block newBlock) {
        //先对新区块的合法性进行校验
        if (isValidNewBlock(newBlock, blockCache.getLatestBlock())) {
          blockCache.getBlockChain().add(newBlock);
          // 新区块的业务数据需要加入到已打包的业务数据集合里去
          blockCache.getPackedTransactions().addAll(newBlock.getTransactions());
          return true;
        }
        return false;
      }
      
      /**
       * 验证新区块是否有效
       * 
       * @param newBlock
       * @param previousBlock
       * @return
       */
      public boolean isValidNewBlock(Block newBlock, Block previousBlock) {
        if (!previousBlock.getHash().equals(newBlock.getPreviousHash())) {
          System.out.println("新区块的前一个区块hash验证不通过");
          return false;
        } else {
          // 验证新区块hash值的正确性
          String hash = calculateHash(newBlock.getPreviousHash(), newBlock.getTransactions(), newBlock.getNonce());
          if (!hash.equals(newBlock.getHash())) {
            System.out.println("新区块的hash无效: " + hash + " " + newBlock.getHash());
            return false;
          }
          if (!isValidHash(newBlock.getHash())) {
            return false;
          }
        }
    
    
        return true;
      }
     }
    

    以上关键代码实现之后,我们就构建了一个非常简单的区块链模型,包含一个基本的区块模型和一个区块链模型,并且能够生成新的区块并添加到区块链中,接下来我们进行测试。

    第五步,我们编写一个Controller类进行调用:
    BlockController.java

    @Controller
    public class BlockController {
    
    
      @Resource
      BlockService blockService;
      
      @Autowired
      BlockCache blockCache;
      
      /**
       * 查看当前节点区块链数据
       * @return
       */
      @GetMapping("/scan")
      @ResponseBody
      public String scanBlock() {
        return JSON.toJSONString(blockCache.getBlockChain());
      }
      
      /**
       * 创建创世区块
       * @return
       */
      @GetMapping("/create")
      @ResponseBody
      public String createFirstBlock() {
        blockService.createGenesisBlock();
        return JSON.toJSONString(blockCache.getBlockChain());
      }
     }
    

    第六步,系统测试
    首先系统启动后,先查看区块链中的数据,可以看到当前系统中的区块链为空:

    然后我们调用创建创世区块的方法,查看返回结果:

    我们把生成的创世区块添加到本地区块链中后,转换成JSON字符串返回,可以看到当前区块链中存储的有一个区块对象,至此我们已经实现了一个简单的区块链。实际的区块链系统模型要复杂的多,需要根据不同的业务场景扩展相应的字段,但是基本特征都是一样的。

    (3)共识机制实现

    在上章节中,我们实现了一个简单的区块链结构,并且能够生成并添加新的区块,但是问题来了,实际的区块链系统是一个多节点、分布式、去中心化的网络,每个节点通过网络交互,实时同步保存着同样的整条区块链数据,那么我们生成的区块,如何才能被其他节点认可,并同步添加到其他所有节点上呢,这个时候我们就需要一套规则,让所有网络节点的参与者达成能够达成共识,接纳并保存新的区块,也就是所谓的“共识机制”。

    理论基础部分已经提到了,共识机制有很多种,各有各的优势与缺点,接下来我们就用java代码来模拟实现我们最为熟知的一种机制:工作量证明(Proof of Work),顾名思义就是对工作量的证明,在基于POW机制构建的区块链网络中,节点通过计算随机哈希散列的数值争夺记账权,求得正确的数值并生成区块的能力是节点算力的具体表现,计算的过程一般被形象地称为“挖矿”。

    简单来说就是,区块链系统设定一套计算规则或者说是一套计算题,在新区块生成前,各个节点都投入到这道题的求解计算中,哪个节点先计算出结果,并得到其它节点的验证和认可,这个节点就会获得新区块的记账权,并获得系统相应的奖励,共识结束。

    典型的PoW共识机制应用就是BTC网络,在BTC网络中,共识计算的目标是找到满足某个特定要求的区块Hash(哈希值)。这个区块哈希值就是工作结果的一个证明,计算工作的目的就是为了寻找到这个证明值,上一章节中,测试时我们已经见过这个Hash值:

    [
        {
            "hash": "25931395e736653212f0258824df4222ae739ec2d5897310258b0857d4d3870c",
            "index": 1,
            "nonce": 1,
            "timestamp": 1580970554734,
            "transactions": [
                {
                    "businessInfo": "这是创世区块",
                    "id": "1"
                }
            ]
        }
    ]
    

    BTC网络PoW使用的Hashcash算法,大致逻辑如下:

    1. 获取某种公开可知的数据data(BTC网络中,指的是区块头);

    2. 添加一个计数器nonce,初始值设置为0;

    3. 计算data与nonce拼接字符串的哈希值;

    4. 检查上一步的哈希值是否满足某个条件,满足则停止计算,不满足则nonce加1,然后重复第3步和第4步,直到满足这个特定的条件为止。

    接下来我们用Java代码实现这个算法,设定满足的特定条件为,Hash值前4位都是0,则计算成功(实际区块链网络中的特定条件要求更高,对计算的运算能力要求也高,并且系统随着计算难度动态调整满足的特定条件,来保证区块生成的速度)。

    第一步,我们新建一个共识机制服务类,添加一个“挖矿”方法,计算成功后,获取记账权,调用添加区块的方法,把区块添加到区块链中:
    PowService.java

    /**
     * 共识机制
     * 采用POW即工作量证明实现共识
     * @author Administrator
     *
     */
    @Service
    public class PowService {
    
    
      @Autowired
      BlockCache blockCache;
      
      @Autowired
      BlockService blockService;
      
      /**
       * 通过“挖矿”进行工作量证明,实现节点间的共识
       * 
       * @return
       */
      public Block mine(){
        
        // 封装业务数据集合,记录区块产生的节点信息,临时硬编码实现
        List<Transaction> tsaList = new ArrayList<Transaction>();
        Transaction tsa1 = new Transaction();
        tsa1.setId("1");
        tsa1.setBusinessInfo("这是IP为:"+CommonUtil.getLocalIp()+",端口号为:"+blockCache.getP2pport()+"的节点挖矿生成的区块");
        tsaList.add(tsa1);
        Transaction tsa2 = new Transaction();
        tsa2.setId("2");
        tsa2.setBusinessInfo("区块链高度为:"+(blockCache.getLatestBlock().getIndex()+1));
        tsaList.add(tsa2);
        
        // 定义每次哈希函数的结果 
        String newBlockHash = "";
        int nonce = 0;
        long start = System.currentTimeMillis();
        System.out.println("开始挖矿");
        while (true) {
          // 计算新区块hash值
          newBlockHash = blockService.calculateHash(blockCache.getLatestBlock().getHash(), tsaList, nonce);
          // 校验hash值
          if (blockService.isValidHash(newBlockHash)) {
            System.out.println("挖矿完成,正确的hash值:" + newBlockHash);
            System.out.println("挖矿耗费时间:" + (System.currentTimeMillis() - start) + "ms");
            break;
          }
          System.out.println("第"+(nonce+1)+"次尝试计算的hash值:" + newBlockHash);
          nonce++;
        }
        // 创建新的区块
        Block block = blockService.createNewBlock(nonce, blockCache.getLatestBlock().getHash(), newBlockHash, tsaList);
        return block;
      }
      
      /**
       * 验证hash值是否满足系统条件
       * 暂定前4位是0则满足条件
       * @param hash
       * @return
       */
      public boolean isValidHash(String hash) {
        //System.out.println("难度系数:"+blockCache.getDifficulty());
        return hash.startsWith("0000");
      }
    }
    

    第二步,编写测试共识机制服务的Controller类方法:
    BlockController.java

    /**
       * 工作量证明PoW
       * 挖矿生成新的区块 
       */
      @GetMapping("/mine")
      @ResponseBody
      public String createNewBlock() {
        powService.mine();
        return JSON.toJSONString(blockCache.getBlockChain());
      }
    

    第三步,启动系统,进行测试。
    首先执行http://localhost:8080/create方法,生成创世区块。
    其次调用http://localhost:8080/mine方法进行工作量计算证明,生成新的区块,并添加到本地区块链中:

    我们来看一下,系统后台计算的过程,此次计算共花费1048ms计算出满足条件的Hash值,共计算4850次:

    至此,我们实现了一个简单的工作量证明机制,并在当前区块链系统节点上运行,完成了正确结果的计算,生成了一个新的区块。

    接下来我们将会开发一个P2P网络,实现多个节点的同时运行,当一个节点挖矿完成后,通过P2P网络广播给其他节点,其他节点验证通过后,会把新产生的区块添加到自己的区块链上,进而保证整个区块链网络所有节点的数据一致性。

    (4)P2P网络开发

    前面我们已经实现了一个基本的区块链系统,并且实现了PoW工作量证明共识机制,通过挖矿计算出正确的结果同时生成一个新的区块添加到区块链中,但是这些都是基于单节点的运行,实际的区块链是有多个节点同时运行的分布式网络系统,所有节点同时计算抢夺记账权,共同维护一条完整的区块链。

    接下来我们基于Java的WebSocket实现一个Peer-to-Peer网络,实现多个节点间的相互通信,通过本章节,我们将要实现以下功能:

      - 创建一个基于java的p2p网络
      - 运行多个节点,且多个节点通过p2p网络自动同步区块信息
      - 一个节点挖矿生成新的区块后,自动广播给其他所有节点
      - 每个节点在接收到其他节点发送的区块内容后,进行验证,验证通过添加到本地区块链上
      - 在自我节点查看整个区块链内容
    

    开发测试本章节的功能,我们最好准备两台电脑或者虚拟机,再或者Docker集群环境也可以,便于多节点的运行测试。如果只有一台电脑也可以,各个节点运行的端口号设置为不相同即可。

    第一步,开发思路整理
    目前我们已经实现了单节点的区块生成,那么我们接下来只需要实现各个节点的消息同步即可。

    • 首先,通过java代码实现p2p网络的server端和client端,每个节点既是服务端也是客户端。

    • 然后,一个节点启动时,会寻找区块链网络上的有效节点,并建立socket连接(BTC网络可以通过使用“DNS”种子方式获取BTC有效节点,DNS种子提供比特币节点的IP地址列表),我们直接把节点列表配置到application.yml文件中。

    • 接着,从连接上的节点获取最新的区块信息,如果当前节点首次运行,则获取整个区块链信息,并替换到本地。

    • 之后,各个节点同时挖矿计算,哪个节点先计算完成,就把生成的新区块全网广播给其他所有节点(实际的区块链网络一直在计算,我们为了便于测试,手动触发一个节点挖矿产生区块,然后全网广播给其他所有节点)。

    • 最后,当一个节点收到广播内容后,对接收到的新区块进行验证,验证通过后添加到本地区块链上,验证的首要条件是新区块的高度必须比本地的区块链高度要高。

    第二步,先实现P2P网络server端
    新建一个P2PServer类,并添加一个初始化server端的方法:
    P2PServer.java

    /**
     * p2p服务端
     * 
     * @author Jared Jia
     *
     */
    @Component
    public class P2PServer {
    
    
      @Autowired
      P2PService p2pService;
    
    
      public void initP2PServer(int port) {
        WebSocketServer socketServer = new WebSocketServer(new InetSocketAddress(port)) {
    
    
          /**
           * 连接建立后触发
           */
          @Override
          public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) {
            p2pService.getSockets().add(webSocket);
          }
    
    
          /**
           * 连接关闭后触发
           */
          @Override
          public void onClose(WebSocket webSocket, int i, String s, boolean b) {
            p2pService.getSockets().remove(webSocket);
            System.out.println("connection closed to address:" + webSocket.getRemoteSocketAddress());
          }
    
    
          /**
           * 接收到客户端消息时触发
           */
          @Override
          public void onMessage(WebSocket webSocket, String msg) {
            //作为服务端,业务逻辑处理
            p2pService.handleMessage(webSocket, msg, p2pService.getSockets());
          }
    
    
          /**
           * 发生错误时触发
           */
          @Override
          public void onError(WebSocket webSocket, Exception e) {
            p2pService.getSockets().remove(webSocket);
            System.out.println("connection failed to address:" + webSocket.getRemoteSocketAddress());
          }
    
    
          @Override
          public void onStart() {
    
    
          }
    
    
        };
        socketServer.start();
        System.out.println("listening websocket p2p port on: " + port);
      }
    }
    
    
    

    第三步,实现P2P网络client端
    P2PClient.java

    /**
     * p2p客户端
     * 
     * @author Jared Jia
     *
     */
    @Component
    public class P2PClient {
      
      @Autowired
      P2PService p2pService;
    
    
      public void connectToPeer(String addr) {
        try {
          final WebSocketClient socketClient = new WebSocketClient(new URI(addr)) {
            @Override
            public void onOpen(ServerHandshake serverHandshake) {
              //客户端发送请求,查询最新区块
              p2pService.write(this, p2pService.queryLatestBlockMsg());
              p2pService.getSockets().add(this);
            }
    
    
            /**
             * 接收到消息时触发
             * @param msg
             */
            @Override
            public void onMessage(String msg) {
              p2pService.handleMessage(this, msg, p2pService.getSockets());
            }
    
    
            @Override
            public void onClose(int i, String msg, boolean b) {
              p2pService.getSockets().remove(this);
              System.out.println("connection closed");
            }
    
    
            @Override
            public void onError(Exception e) {
              p2pService.getSockets().remove(this);
              System.out.println("connection failed");
            }
          };
          socketClient.connect();
        } catch (URISyntaxException e) {
          System.out.println("p2p connect is error:" + e.getMessage());
        }
      }
    }
    

    第四步,定义P2P网络同步的消息模型
    同步的消息模型,我们定义为四类,分别是:
    BlockConstant.java

      // 查询最新的区块
      public final static int QUERY_LATEST_BLOCK = 1;
    
    
      // 返回最新的区块
      public final static int RESPONSE_LATEST_BLOCK = 2;
    
    
      // 查询整个区块链
      public final static int QUERY_BLOCKCHAIN = 3;
    
    
      // 返回整个区块链
      public final static int RESPONSE_BLOCKCHAIN = 4;
    

    定义一个各个节点间传递的消息模型:
    Message.java

    /**
     * p2p通讯消息
     *
     * @author Jared Jia
     * 
     */
    public class Message implements Serializable {
      
        private static final long serialVersionUID = 1L;
        /**
         * 消息类型
         */
        private int type;
        /**
         * 消息内容
         */
        private String data;
        
        /****set get方法省略****/
        
      }
    

    第五步,实现向其他节点广播的方法

    新建一个p2p网络服务类,向外发送消息,或者处理当前节点收到其他节点发送的请求。
    P2PService.java

    /**
       * 全网广播消息
       * @param message
       */
      public void broatcast(String message) {
        List<WebSocket> socketsList = this.getSockets();
        if (CollectionUtils.isEmpty(socketsList)) {
          return;
        }
        System.out.println("======全网广播消息开始:");
        for (WebSocket socket : socketsList) {
          this.write(socket, message);
        }
        System.out.println("======全网广播消息结束");
      }
    

    第六步,开发消息处理路由

    第五步中已经实现了向外发送消息,本步骤实现接收消息。
    首先设计一个服务端和客户端共用的消息路由,来分发消息给对应的处理单元。
    P2PService.java

    /**
       * 客户端和服务端共用的消息处理方法
       * @param webSocket
       * @param msg
       * @param sockets
       */
      public void handleMessage(WebSocket webSocket, String msg, List<WebSocket> sockets) {
        try {
          Message message = JSON.parseObject(msg, Message.class);
          System.out.println("接收到IP地址为:" +webSocket.getRemoteSocketAddress().getAddress().toString()
              +",端口号为:"+ webSocket.getRemoteSocketAddress().getPort() + "的p2p消息:"
                  + JSON.toJSONString(message));
          switch (message.getType()) {
          //客户端请求查询最新的区块:1
          case BlockConstant.QUERY_LATEST_BLOCK:
            write(webSocket, responseLatestBlockMsg());//服务端调用方法返回最新区块:2
            break;
          //接收到服务端返回的最新区块:2
          case BlockConstant.RESPONSE_LATEST_BLOCK:
            handleBlockResponse(message.getData(), sockets);
            break;
          //客户端请求查询整个区块链:3
          case BlockConstant.QUERY_BLOCKCHAIN:
            write(webSocket, responseBlockChainMsg());//服务端调用方法返回最新区块:4
            break;
          //直接接收到其他节点发送的整条区块链信息:4
          case BlockConstant.RESPONSE_BLOCKCHAIN:
            handleBlockChainResponse(message.getData(), sockets);
            break;
          }
        } catch (Exception e) {
          System.out.println("处理IP地址为:" +webSocket.getRemoteSocketAddress().getAddress().toString()
            +",端口号为:"+ webSocket.getRemoteSocketAddress().getPort() + "的p2p消息错误:" 
            + e.getMessage());
        }
      }
    

    第七步,开发消息处理单元

    有了消息路由之后,接着编写不同的处理单元,处理其他节点发送来的区块或者区块链信息,总体原则是:先校验其他节点发送来的区块或者区块链的有效性,然后判断它们的高度比当前节点的区块链高度要高,如果高则替换本地的区块链,或者把新区块添加到本地区块链上。
    P2PService.java

    /**
       * 处理其它节点发送过来的区块信息
       * @param blockData
       * @param sockets
       */
      public synchronized void handleBlockResponse(String blockData, List<WebSocket> sockets) {
        //反序列化得到其它节点的最新区块信息
        Block latestBlockReceived = JSON.parseObject(blockData, Block.class);
        //当前节点的最新区块
        Block latestBlock = blockCache.getLatestBlock();
        
        if (latestBlockReceived != null) {
          if(latestBlock != null) {
            //如果接收到的区块高度比本地区块高度大的多
            if(latestBlockReceived.getIndex() > latestBlock.getIndex() + 1) {
              broatcast(queryBlockChainMsg());
              System.out.println("重新查询所有节点上的整条区块链");
            }else if (latestBlockReceived.getIndex() > latestBlock.getIndex() && 
                latestBlock.getHash().equals(latestBlockReceived.getPreviousHash())) {
              if (blockService.addBlock(latestBlockReceived)) {
                broatcast(responseLatestBlockMsg());
              }
              System.out.println("将新接收到的区块加入到本地的区块链");
            }
          }else if(latestBlock == null) {
            broatcast(queryBlockChainMsg());
            System.out.println("重新查询所有节点上的整条区块链");
          }
        }
      }
      
      /**
       * 处理其它节点发送过来的区块链信息
       * @param blockData
       * @param sockets
       */
      public synchronized void handleBlockChainResponse(String blockData, List<WebSocket> sockets) {
        //反序列化得到其它节点的整条区块链信息
        List<Block> receiveBlockchain = JSON.parseArray(blockData, Block.class);
        if(!CollectionUtils.isEmpty(receiveBlockchain) && blockService.isValidChain(receiveBlockchain)) {
          //根据区块索引先对区块进行排序
          Collections.sort(receiveBlockchain, new Comparator<Block>() {
            public int compare(Block block1, Block block2) {
              return block1.getIndex() - block2.getIndex();
            }
          });
          
          //其它节点的最新区块
          Block latestBlockReceived = receiveBlockchain.get(receiveBlockchain.size() - 1);
          //当前节点的最新区块
          Block latestBlock = blockCache.getLatestBlock();
          
          if(latestBlock == null) {
            //替换本地的区块链
            blockService.replaceChain(receiveBlockchain);
          }else {
            //其它节点区块链如果比当前节点的长,则处理当前节点的区块链
            if (latestBlockReceived.getIndex() > latestBlock.getIndex()) {
              if (latestBlock.getHash().equals(latestBlockReceived.getPreviousHash())) {
                if (blockService.addBlock(latestBlockReceived)) {
                  broatcast(responseLatestBlockMsg());
                }
                System.out.println("将新接收到的区块加入到本地的区块链");
              } else {
                // 用长链替换本地的短链
                blockService.replaceChain(receiveBlockchain);
              }
            }
          }
        }
      }
    

    3、完整系统运行与测试

    第一步,打包生成测试用的可执行jar包
    准备两台机器(虚拟机或者Docker集群都行),同时运行两个节点,节点信息如下:

    通过mvn package -Dmaven.test.skip=true命令对工程进行打包,生成可直接运行的jar包,打包前对工程进行配置,配置信息如下图:

    节点Node1打包:

    节点Node2打包:

    分别打包之后,生成两个节点的可执行jar包,如下:

    把两个jar包分别放在对应IP的windows机器上,打开命令行模式,进入jar所在文件夹,分别执行以下命令运行两个节点:
    java -jar dce-blockchain-node1.jar
    java -jar dce-blockchain-node2.jar

    启动节点2的时候,可以看到后台日志,已经连接上节点1,如下图所示:

    第二步,对两个节点进行测试

    首先,两个节点启动后,用postman分别执行http://192.168.0.104:8080/scan和http://192.168.0.112:8090/scan请求,可以看到两个节点的区块链内容都为空。

    然后,在节点1上分别执行http://192.168.0.104:8080/create和http://192.168.0.104:8080/mine请求,来生成创世区块,以及通过挖矿产生第二个区块,执行后查看节点1的区块链信息如下:
    执行http://192.168.0.104:8080/scan结果:

    [
        {
            "hash": "5303d2990c139992bdb5a22aa1dac4f2719755304e45bac03ca4a1f1688c909e",
            "index": 1,
            "nonce": 1,
            "timestamp": 1581064647736,
            "transactions": [
                {
                    "businessInfo": "这是创世区块",
                    "id": "1"
                },
                {
                    "businessInfo": "区块链高度为:1",
                    "id": "2"
                }
            ]
        },
        {
            "hash": "0000de5eea0c20c2e7d06220bc023886e88dd8784eaa2fd2d1d6c5e581061d85",
            "index": 2,
            "nonce": 4850,
            "previousHash": "5303d2990c139992bdb5a22aa1dac4f2719755304e45bac03ca4a1f1688c909e",
            "timestamp": 1581064655139,
            "transactions": [
                {
                    "businessInfo": "这是IP为:192.168.0.104,端口号为:7001的节点挖矿生成的区块",
                    "id": "1"
                },
                {
                    "businessInfo": "区块链高度为:2",
                    "id": "2"
                }
            ]
        }
    ]
    

    最后,我们来验证节点2是否已经完成了节点1生成的区块链信息的网络同步,Postman执行http://192.168.0.112:8090/scan请求,查看返回结果:
     从结果可以看到,区块链网络节点2已经接收到节点1发送的区块链信息,系统日志如下:

    反过来,我们在节点2上再执行一次挖矿操作,可以看到节点1上,已经接收到节点2挖矿新产生的区块信息,并添加到节点1的区块链上:

    至此,我们已经实现了一个完整的小型区块链网络,并实现各个节点间的通信,多个节点共同维护同一个区块链信息。

    结语:
    区块链系统非常庞大,涉及方方面面的技术,本人所演示的代码主要对区块链基础的一些概念进行了诠释,感兴趣的同学,还可以在此基础上继续开发,来实现例如持久层、消息的加密解密、系统账户模型、预言机、侧链技术以及智能合约等区块链系统功能。


    写给每个区块链技术人员:
    目前市面上流行的企业级区块链框架,例如超级账本Fabric都是国外人员在主导,而我们国内除了几家大厂外,其他很多区块链公司基本都是把人家的东西拿过来进行二次封装,然后对外声称自己公司已经掌握了区块链核心技术,并对企业提供服务,这是一种不好的现象。大家可以想想我们现在用的开发语言、框架有几个真正是国产的,我们再联想一下前段时间中兴、华为被人家核心技术卡脖子事件,就知道我们要做的事情有很多,我们需要去除浮躁,静下心来好好研究底层核心技术,这样才能实现真正的“弯道超车”!

    三、源代码

    本文完整源代码地址:

    https://gitee.com/luckytuan/dce-blockchain

    PS:如果觉得我的分享不错,欢迎大家随手点赞、在看。

    END

    展开全文
  • 互联网进入了下半场时代,由于科技的进步与行业的竞争,企业获得用户的成本正在急剧推高,大家都面临着获客难、...今天黎想老师为大家讲述:如何建立一套完整的用户成长体系? 那么建立一套完整的用户成长体系或许能解

    互联网进入了下半场时代,由于科技的进步与行业的竞争,企业获得用户的成本正在急剧推高,大家都面临着获客难、费用高、留存差、频度低等相关问题。

    青岛艺形艺意文化传媒有限公司创始人,青岛首席商学院新媒体运营创始人,资深新媒体运营人,IT行业观察者,新型互联网+营销实践者,金牌文案创作者,腾讯网、百度网、今日头条、搜狐网、网易新闻网、凤凰新闻网等网站知名专栏作者,《新媒体推广运营实战大全》 作者、知名品牌营销顾问黎想。今天黎想老师为大家讲述:如何建立一套完整的用户成长体系?
    在这里插入图片描述

    那么建立一套完整的用户成长体系或许能解决部分企业问题。

    搭建一套完整的用户成长体系的意义:用户都需要激励,平台必须建立用户成长体系,才能更有效地留住用户,促进用户活跃,增强用户粘性。

    用户运营人员通过各种手段拉来了用户,后续就需要对这些用户进行维护、管理,提高他们使用产品的时间、频率,提高产品粘性和用户生命周期。

    用户成长体系如何建立

    用户成长体系应该是贯穿整个产品周期的,一个完整的用户成长体系,对处在不同阶段的用户可以起到不同作用,一旦设计好后最好不要轻易改动。

    首先明确两点:什么样的产品适合做用户用户成长体系?什么时间段引入用户用户成长体系比较合适?

    开篇我们就说过,用户成长体系的核心在激励。对于强烈刚需的产品,即使不激励,用户也会主动使用,因此,高频、需求没有那么强烈的产品更适合建立用户等级体系。

    第二点,什么时间建立。
    用户等级用户成长体系从本质上而言是一个管理用户的系统,这个系统我们可以在设计产品之初就进行规划,但只有拥有了一定基数用户时它才能真正起到作用。

    同时,我们也能根据已有的用户数据设计出更加适合产品的用户体系。
    在这里插入图片描述

    1. 用户成长体系建立的九大原则

    体系应需要体现平台核心业务特色:其中的元素和机制应该尽量体现独特性,而不是套哪都能用的东西。

    体系投入产出需要平衡:预防投入过大的成本,只获取微薄的收益,或没有经过精细计算的成长机制,是不把企业逼向死亡的重要一环。

    经验体系需要分离:一个公司内不同的业务体系之间的成长体系和货币体系要分离开来,不同业务体系拥有不同的成长体系,可以更加灵活不受限制,运营起来更加方便。

    用户需要分类:不同用户使用不同方式激励,可把用户分为新用户、普通用户、活跃用户、核心用户等。

    体系可被拓展:假如等级较少,用户容易满级,这个体系就失去原有的价值和作用了。用适当的方式,实现体系的可持续发展,是一个决定体系优劣的重要因素。

    门槛可被进入:入门门槛太高,难度太大,容易失去激励用户的意义,开始的第一步尤其要注意不要让用户望而生畏。

    激励可被感知:不能骚扰用户,但是也不能一点都不通知用户,适时地强调用户成长体系的存在感,体现不同的差异化和可展现性,用户才有动力去玩。

    等级可被期待:用机制和玩法保持稀缺感,同时可考虑增加降级或下限机制:如连续12个月不发言将销号、未完成任务降级等。

    “刷假”可被预防:包括每日任务只能做多少次,特定行为只计算第一次有效,重复同样内容不计数等,也是有效保证体系稀缺感的做法。

    2. 用户成长体系搭建的核心6步
    在这里插入图片描述

    将用户按照用户行为进行分层与分级别,青铜、白银、黄金、钻石、荣耀、星耀等等不同的奖励对应不同福利系统,或者对应权益不同,培养粉丝系统。
    在这里插入图片描述

    针对不同层级的用户,判断哪些手段能达成我们的目标,目标如促活、促交易、促留存等等。针对不同的目标,我们在想行为手段,如相关活动复购、充值、返现、购卡、积分、次开、包月卡,如美团的免运费、兑换等,并构建核销场景。

    在这里插入图片描述

    确定了用户层级、阶段目标、测试手段,然后需要解决的是触达渠道。接下来是展现形式,这里就涉及到文案配图、APP弹窗流程、短信连接、海报二维码流程等等。
    在这里插入图片描述

    最后是数据反馈与数据收集,及时反馈与事后复盘的分析行为。
    3. 用户成长体系的分类与定位

    这里面我们涉及到一个定位经典的POM原则分析自身用户成长体系(经典的三类体系):福利驱动,内容驱动,功能驱动

    企业要根据自身的产品功能和用户诉求进行用户成长体系的选型,并随着企业的成长持续运营它,持续的运营才有持续的价值,切忌放任不管。福利规则的制定往往代表着企业的意志,是企业希望看到的发展方向。

    总结

    用户用户成长体系是一套独立于产品核心功能价值之外,但是又融合在用户和产品之中的产品连接力。

    好的用户用户成长体系,会很好的促进用户活跃,增强用户粘性,提升产品的商业价值。产品发展到一定阶段,都需要着手构建自己的用户用户成长体系,进一步提升产品价值。

    在这里插入图片描述

    把用户成长体系建立起来后,跟很多产品一样,还需要花时间去研究各层的转化。根据数据用户行为引导以及奖励,如果用户成长体系在初期呈现较好的数据转化,还需要准备在APP中增加用户感知,做活动、提升入口等等。

    将基础产品建立起来不是难事,把产品建立起来之后,根据数据及实际需求去运营调整才是最花精力最考验运营人的事情,希望与各位共勉。

    展开全文
  • 交易系统完整的交易规则体系。一套设计良好的交易系统,必须对投资决策的各个相关环节作出相应明确的规定。这种规定必须是客观的、唯一的,不允许有任何不同的解释。一套设计良好的交易系统,必须符合使用者的...

    在风险市场的赢家都有自已的交易系统,这个交易系统是非机械的,适合你自己个性的,有完善的交易思想、细致的市场分析和整体操作方案的。因此寻找适合自已的交易系统与完善自已的交易系统是专业交易人士投资的一生几乎每天都在做的一件事。

     

    《什么是交易系统?》

           交易系统是完整的交易规则体系。一套设计良好的交易系统,必须对投资决策的各个相关环节作出相应明确的规定。这种规定必须是客观的、唯一的,不允许有任何不同的解释。一套设计良好的交易系统,必须符合使用者的心理特征、投资对象的统计特征以及投资资金的风险特征。

           交易系统的特点在于它的完整性和客观性。它保证了交易系统结果的可重复性。从理论上来说,对任何使用者而言,如果使用条件完全相同,则操作结果完全相同。系统的可重复性即是方法的科学性,系统交易方法属于科学型的投资交易方法。

           大部分投资人往往把决策的重点放在对市场的分析和判断上,其实这是非常偏颇的。成功的投资不但需要正确的市场分析,而且需要正确的风险管理和正确的心理控制。三者之间心理控制是最重要的,其次是风险管理,再次才是分析技能,即所谓的3M系统(Mind、Money、Market)。

           如果用一个比方来形容,对市场的判断在投资行为的重要性中只占1%而已,被大多数投资人忽略的东西,才是投资行为中的决定性因素。市场分析是管理的前提,只有从正确的市场分析出发,才能建立起具有正期望值的交易系统。

           风险管理只有在正期望值的交易系统下才能发挥其最大效用,而心理控制正是两者的联系桥梁和纽带。一个人如果心理素质不好,则往往会偏离正确的市场分析方法,以主观愿望代替客观分析,也常常会背离风险管理的基本原则。

           投资人若想在效率市场持续稳定的赢利,必须成功的解决两大问题:

           1、如何在高度随机的价格波动中寻找非随机的部分;

           2、如何有效的控制自身的心理弱点,使之不致影响自己的理性决策。很多投资家的实践都证明,交易系统在上述两方面都是投资人的有力助手。

           大多数投资者在进入市场的时候,对市场的认识没有系统的观点。很多投资人根据对市场的某种认识,就片面的承认或否认一种交易思路的可行性,其实他们不知道,要想客观的评价一种交易方法,就要确认该方法在统计概率意义上的有效性。

           无论是随机还是非随机的价格波动中不具备统计意义有效性的部分,只能给投资人以局部获胜的机会而没有长期稳定获胜的可能。而交易系统的设计和评价方式可以帮助投资者有效的克服对方法认识的盲目性和片面性。

           交易系统还可以帮助投资人有效的控制风险。实践证明,不使用交易系统的投资人,难以准确而系统的控制风险。没有交易系统做指导时,投资人很难定量评估每次进场交易的风险,并且很难评估单次交易的风险在总体风险中的意义。

           而交易系统的使用,可以明确的告诉投资人每次交易的预期利润率、预期损失金额、预期最大亏损、预期连续赢利次数、预期连续亏损次数等,这些都是投资风险管理的重要参数。

           帮助投资人有效的克服心理弱点,可能是交易系统的最大功用。交易系统使交易决策的过程更加程序化、公开化、理性化。投资人可以从由情绪支配的处于模糊状态的选择过程转变为定量的数值化的选择过程,即单纯判定信号系统的反映以及执行信号所代表的决策。

    交易系统几个核心内涵

           1、心态核心

           在交易系统没有提出可交易品种时期,心态如何摆正,并且做到行与心合一,是交易系统能够发挥系统交易的首要条件。如果,一套很好的交易系统,但心态急噪,无法忍耐空仓或者视那些持续飚升但不知道如何控制风险才为合理而又强行介入。

           那么,作为脱离交易系统控制,导致的失败,就不能归咎于交易系统程序失败,是心态失败导致了交易失败。因此,我认为,心态是最重要的,决定了交易系统的成败。

           2、得失核心

           不同的资金起点,有不同的得失。如100万与3万,年一倍,其交易次序是一致的,但掌握100万的个体,其将收益目标降低到年50%,其收益高于3万翻倍许多,其心理要求和技术要求就会大幅度的降低。

           因此,导致了不同的交易系系统。100万的个体很有可能看重中线交易系统,3万的个体很有可能看重短线交易。

           3、技术核心

           市场获利模式就三种,超跌反弹、高抛低吸、强势追高。

           (1) 超跌反弹,超,超到什么程度必反?弹,弹到什么程度必跌?

           (2) 高抛低吸,高,高到什么程度为高?低,低到什么程度为低?吸,吸是一次还是多次?

           (3) 强势追高,强,什么时期可以追,什么时期不能追?追,高到什么程度还可以追?

           超跌反弹,不同的人有不同的分析基点,那么,定义这个超,就可以采用历史统计来实现。例如,高点下降超过60%,并且在形态、成交量分布等等技术,都达到适当,那么,这个超,就是必反的定义。历史统计应该成功率非常高才对,如果,还是很低,那么,这个就不是超。

           高抛低吸,笔者认为,从形式上,它应该是某种通道的产物,达到通道的上轨,抛出,达到通道的下轨,低吸(在你的系统中有使用布林线进行操作,但必须分析整个趋势处在什么状态,如果处在整理趋势之中是很可行的一种技术分析指标。

           但如果明显处在一个上升或下降的趋势之中,那么使用趋势线与通道线是明智的选择——当然在整理趋势中也适用,这样避免使用布林线等摆动指数所发出的模糊或错误信号)。

           通道的下轨永远都都在K线之下,出现小概率在之上,应该是抄底系统信号。通道的上轨永远都在K线之上,出现小概率在之下,应该是逃顶系统信号。——与布林线有同曲异工之妙。

           强势追高,当指数形成中级行情的时候,才追高,这种是比较安全的。也可以在下降通道中追高,但这要取决于历史统计,实际上,强势追高是一种不理性的操作手法。在追高的选股时期,可以肯定手中有资金,行情在上涨,这部分资金踏空,那么,如果有上面两种交易系统,就不存在踏空。只存在速度上的不同。

           4、控制核心

           在交易系统出现信号时期,因为必然存在不确定性,就需要资金管理来将不确定性(我称为风险)降到最大可控程度,这个并不是技术交易系统的内容。假设,一个可以达到70%成功率的技术交易系统,如果加入资金管理,可以提升到80%,那么,这个技术交易系统的成功率就是80%,而不是70%。

           5、跟踪核心

           在交易系统出现信号时期,并交易介入。后市趋势跟踪系统是否有转市的可能存在,如果存在,即立刻止赢。因此,好的交易系统,还应该有一个配套的好的趋势跟踪系统存在,以决定趋势的终结,以便于让利润奔跑。

           6、空仓核心

           当交易系统没有信号时期,是否能够达到空仓所需要的心理素质,这也是交易系统成败的重大问题。

           由此,可以清晰看到,技术交易系统只是交易系统的一个部分,而不是全部。当技术交易系统出现信号时期,并不是系统在做决策,实际上是人在综合做出行为决策。

           一份好的交易系统,包含了心态、技术、要求、忍耐、控制等等。所以,交易系统是综合分析系统。来解决在正确的什么时机、选择正确什么对象、进行正确的行为的决策系统。

           建立交易系统总体流程步骤一:“构造交易系统”

           A) 要明确交易系统的目的:“克服人性弱点,便于知行合一”

           B) 要明确交易系统的特性:“整体性和明确性”

           C) 交易系统随时间和市场外部环境变化,“本身要能够修改和进行参数调整”

           D) 交易系统的一些基本子系统:“行情判断、板块动向、风险管理、人性控制”

           建立交易系统总体流程步骤二:“检验交易系统”

           A) 检验交易系统包括:“统计检验、外推检验和实战检验”;

           B) 要考虑交易成本;

           C) 要考虑建仓资金量大小造成的回波效应;

           D) 要考虑小概率事件(统计学上的胖尾)对交易系统的影响;

           建立交易系统总体流程步骤三:“执行交易系统”

           A) 日常操作主观要服从客观,“交易有依据、欲望要消除”

           B) 模拟操作不可少,即使不交易,依然要“仔细看盘、仔细复盘、揣摩多空主力的思路、勤动脑多实践”,最终做到“正确地知行合一”

           总之:一个交易系统的形成除了有市场普遍性具有的特点外,也应有每个人个人的性格特点,对于即日交易(秒——小时)、短线(小时与天)、中线(周与月)、长线(月与年)不同交易方式的人(其中已含有个人的操作特点)也应有所不同。

           交易成功的全部秘诀就是:始终如一地坚持用自己的交易系统来交易。

           投资的成功,重要的不是看你的工具有多么强大和优秀,而是你能否用好你的交易工具。在通往财富梦想的道路上,最有效的策略就是专注和坚持一个良好的交易系统。专注和坚持可以产生不可思议的力量。当你真正做到这一点时,你就可以创造你自己都不能相信的奇迹。

           任何领域的伟大,都来源于一个人坚持到所有障碍都被克服之后的能力,由于普通人完全缺乏这种决心和能力,因此他们几乎注定只能是普通和平凡。

           坚持使用一个成功的交易系统,是小人物成就大事业的不二选择。任何交易者都必须有将小事做成大事业的决心和信心。不要轻易怀疑自己已经经过验证的良好的交易系统。

           超级操盘手比的往往不是技术,而是素质。

           成功的交易者有着一种与众不同的素质,他们拥有正确的思维方式,严谨的交易态度,强烈的自信心、果敢,和面对失败永不言败的精神,即使在市场最困难的时候,他们也能完全按照系统交易。

           因为他们知道:成功就是要有远大的目光,克服人性短视的弱点,有坚持一个固定的盈利模式的耐心和信心。

           贪婪和恐惧的深层次原因,在于交易理念混乱和资金管理不合理。

           在我们人的一生当中,有两种情绪一直主导着我们的生活,甚至可以说是决定着我们的命运——贪婪和恐惧。投资是生活的一个缩影,是一个被缩小了的生活。

           置身于投资世界的人们,同样面临着恐惧和贪婪的诱惑和驱使。投资世界赤裸裸的金钱交易,更加放大了这种情绪。以至于我们的思维、心情在恐惧和贪婪中完全迷失自我。

           其实在操作时,我们不要去奢望能在趋势中捕捉到多空两面的全部行情。而是完全按照主力的思维只操作单边行情。市场是永不停息的,即使我们错过了一次好的机会,只要我们有耐心,能够控制住内心的贪婪,总会等到下一次良好的交易时机。

           但实际上,我们中的大多数人永远控制不住自己内心的贪婪,在丢掉合理的入场时机后盲目的追涨杀跌。还有很多次,我们会奈不住自己内心的恐惧,在系统没有出现止损的情况下,人为的平掉了本可以大赚的仓位。

           几乎所有失败的交易者都会因为交易理念的混乱,不懂得交易盈利的哲学原理,致使他们不能接受系统中出现的亏损,从而也就享受不到大幅盈利所带来的震撼。

           在交易中,亏损只是我们为了获得最终胜利所必须付出的成本和代价,它就像人类的呼吸一样自然。然而很多投资者在经历了几次连续的亏损后,往往会因为对未来不可预见的恐惧感,从而放弃了一套优秀的交易系统。

           一个成功的交易者,必须要培养起正确对待亏损的认识和心态。只有你恰如其分地认识到了亏损在整个交易系统中的意义,才能在一种无压力的心态中进行当下的操作。

           除非你能够从交易理念上坦然接受亏损,否则你永远不会成为一名持续盈利的交易者。为了追求全局的盈利,就必须接受暂时的亏损。

           以上种种怪异的交易行为,都是因为投资者的交易理念不清晰,不懂得投资行业盈利的交易哲学造成的。试想,如果我们有着正确的交易理念,能够深刻的理解成功的交易其实不关乎趋势多空的判断,而在于严格的遵守纪律。

           那么我们还会在系统发出做多信号时对趋势的多空摇摆不定吗?如果我们能够深刻的理解了交易和趋势无关,只与交易信号有关,我们还会心神不宁的看外盘、听消息、在盘中随意地操作吗?

           如果我们深刻地理解了投资盈利的交易哲学,亏损只是我们捕捉成功的必由之路,我们还会不接受亏损、而去追求和寻找高胜率的交易系统吗?

           如果我们懂得了所有的交易系统只能捕捉特定的一段行情,任何系统都不可能将所有的波动一网打尽,懂得做得多不一定收益就多的道理,我们还会贪婪的想拥有所有的利润而频繁操作吗?

           交易与其他无关,仅仅与系统的交易信号有关。趋势的方向根本就和你的成功无任何关联。什么看外盘、听消息,问专家,这些除了使自己更加焦虑以外,对我们的成功交易没有丝毫的帮助。

           只要系统显示的是做空信号,哪怕全世界的人都看多,我们也只能做空。严格的按照系统操作才能保证亏损最小化,利润最大化。有一句名言:“一个优秀的操盘手是一个没有观点的操盘手”。

           还有一种情况会导致我们恐惧和贪婪,那就是资金使用不合理。

           重仓、满仓、不断加仓会使自己的操作资金承担过大的风险,这种赌徒式的操作方式只能导致两种结果:要么暴利,要么爆仓。从而使交易者由于心理极度的恐惧,在行情还没有到达指定的止损位置时,就匆匆平仓出局。从而导致整个操作变形,或由于恐惧从而放弃一个原本良好的操作系统。

           交易是一种在收益和风险中寻求平衡的学科。我们既不能一味使用重仓,也不能一直使用轻仓。而在该轻的时候轻,该重的时候重。不但要有定性,还要有定量,这样才能指导实际的操作。随着交易系统和盈利多少不断变化交易的仓位,这才是资金管理的精髓。

           交易带给我们的不只是金钱,更多的是智慧的彻悟和人性的归真!

           拥有一套良好的交易系统是起点,执行力才是难点,自己有切实可行的交易策略才是重点。通过不断细化和完善交易系统的细节,以适应自己的交易性格,让自已的执行力大大改善。交易的时候如行云流水、心理没有丝毫的压力,交易变得舒适和自然。

           对于投资世界中的纪律问题,我们往往能够通过细化交易系统,以适应自己的性格;设计优秀的资金管理,以减轻交易时的心理压力;修炼资金的交易内功,以防止对投资的不正确认识导致的随意操作;固化交易流程,以便能够轻松地遵守交易纪律。

           技术只是成功道路上一个很微不足道的障碍,良好技术应用背后是交易内功。

           在得到一套大概率盈利的交易系统后,在实际操作中往往却因为这样或者那样的原因不能完全执行。到底是什么在影响着我们对一个优秀的交易系统的执行呢?是交易者彻底贯彻交易系统的信念和能力。

           控制着交易成败的关键因素就是信念,自信心的大小决定一个人成就的大小。一个人成就的大小,往往不会超出他自信心的大小。成功的先决条件就是自信。

           交易内功:

           (一) 盈利的交易哲学

           (二) 投资者看待自我和看待市场的方法

           (三) 正确的交易理念

           (四) 投资者对待投资市场的态度和坚定的交易信念

           (五) 实战中的交易行为和交易流程

           很多人拥有了盈利的交易系统却根本无法执行,很大程度上是因为他们怀疑系统,对自己的交易系统没有坚定的信念。“我们不但要有交易系统,还要有信念系统。”

           在交易中,我们如何对自己的交易系统形成坚定的信念呢?答案很简单,那就是熟悉系统、理解系统、融合系统。交易技术和交易哲学是相为一体的,是交易哲学思想的具体体现,只有这样的技术才能根深蒂固,应用起来才能行云流水,而不会障碍重重。

           投资者只有将操盘技能训练成一种交易习惯,训练到极其熟练以至于不经过思考的境界,才能真正抵制住盘面这种迷人的诱惑,才能完全达到按系统信号操作而不变形的能力。

           投资是一场马拉松,而不是一场百米赛。在这场旷日持久的比赛中,看的不是谁跑的最快,而是看到底谁跑的最稳。

           我们只有长期的专注于一个我们最为熟悉的交易模型,这样我们就能很快的熟悉这个模型,并和这个模拟形成某种程度的默契,使自己的心率能够和市场的波动相匹配,才能在交易中达到一种舒适、自然的境界。

           投资是一种游戏,轻松、简单和按部就班才能在竞争中达到盈利。如果你在每天的交易中都感到焦虑和恐慌,心神不定,那么肯定是你的交易方法和你的心率没有和这个品种形成共振,达到一种默契的境界,这样你就很难达到投资获利的目的。

           交易就是在不断的和自己的人性作斗争中逐渐成长。只有你将原本不舒服的交易变得逐渐的自然流畅,你才能够成为一名成功的操盘手。

    推荐阅读:

    1.一个量化策略师的自白(好文强烈推荐)

    2.股票期货经典的量化交易策略都在这里了!(源码)

    3.期货/股票数据大全查询(历史/实时/Tick/财务等)

    4.三分钟弄明白为什么贝叶斯是量化工作者最常用的工具

    5.学习Python有哪些书籍?这里有一份书单送给你

    6.江湖中常说的“网格交易法”到底是什么?

    7.10种经典的日内交易策略模型思路

    8.干货 | 量化选股策略模型大全

    9.量化金融经典理论、重要模型、发展简史大全

    展开全文
  • 背景作为一款国民级二手交易App,闲鱼每天都有成千上万的二手闲置商品发布,精准的个性化的商品推荐是促进闲鱼用户快速成交的秘诀。搜索推荐算法的精准和埋点数据的准确性息息相关。一旦埋点数据出...
  • 系统性能测试概述... 1 1.1 性能测试概述... 1 1.2 性能测试的指标... 2 1.3 关键点的描述... 2 1.4 性能测试的目的... 2 1.5 测试项目开发规范... 2 二 使用VS压力测试工具进行测试... 3 ...
  • 四、申请微信支付 在线交易,就需要申请微信支付,有两种申请方式: 1、需要去微信支付官方填写资料,申请下来费率是千分之六,需要配置API密钥,API证书、JSapi支付,需要绑定小程序,绑定得有店。 不花钱,但流程...
  • 介绍什么是交易系统,教你建立自己的交易系统
  • 网上图书交易系统

    2013-05-19 19:47:05
    ASP.NET的网上图书交易系统一套完整的代码,有数据库的网站课程或者毕业设计
  • 海龟交易法则(中译文)

    千次阅读 2018-07-06 21:46:20
    完整的交易系统一套完整交易系统的成分市场头寸规模入市止损离市策略摘要 第二章 市场 第三章 头寸规模波动性—-N的含意价值量波动性调整单位范例头寸规模的重要性用单位度量风险 第四章 入市突破增加单位...
  • 给大家分享一套课程——【完整版8章】首门程序员理财课 Python量化交易系统实战(2021最新课程),希望对大家学习有帮助,给个好评哦
  • TCPIP完整一套基础介绍

    千次阅读 2012-12-11 11:01:44
     在完整一组CClass中如203.67.10.0-203.67.10.255NetMask255.255.255.0, 203.67.10.0称之NetworkNumber(将IPAddress和Netmask作AND),而 203.67.10.255是Broadcast的IPAddress,所以这?两者皆不能使用,实 际只能...
  • TCP/IP完整一套基础介绍

    千次阅读 2006-10-16 12:03:00
    TCP/IP完整一套基础介绍TCP/IP应该是个协议集,根据OS的七层理论,TCP/IP分为四层.分别是应用,传输,Internet和网络界面. 我们一般说TCP在传输层,而IP在Internet层. TCP/IP的应用程序包括我们平时经常用到的...
  • 校园商品交易系统

    2017-01-07 17:13:23
    一套完整的Android系统 内容包括完整可以执行的源代码,Android完整课程设计报告书,没有任何错误,是大家Android课程设计以及毕业设计的最佳选择。
  • 海龟交易系统源码

    千次阅读 2015-07-30 03:12:00
    一套完整的机械的交易系统都有明确并且唯一的交易信号,例如两条均线就构成了一个交易系统,只不过它更好的说  海龟交易系统相对而言是一个比较早的交易系统了,也是世界著名的机械交易系统,对于想学习程序化...
  • 交易系统的核心内涵

    2020-04-23 11:18:47
    01、良好的交易系统 交易的秘密就是找到一套适合你的交易系统。这个交易系统是非机械的,适合你自己个性的,有...交易系统完整的交易规则体系。一套设计良好的交易系统,必须对投资决策的各个相关环节作出相应...
  • 饿了么交易系统 5 年演化史

    千次阅读 多人点赞 2020-09-18 21:50:37
    作者介绍:杨凡,花名挽晴,饿了么高级架构师,2014 年加入饿了么,2018 年随饿了么被阿里巴巴收购一同加入阿里巴巴,4 年团队管理经验,4 年主要从事饿了么交易系统建设,也曾负责过饿了么账号、评价、IM、履约交付...
  • 首先来聊聊往事吧~~两年前就职于一家传统金融软件公司,为某交易所开发一套大型交易系统,交易标的的价格为流式数据,采用价格触发成交方式,T+0交易制度(类似炒股,只是炒的不是股票而是其他标的物,但可以随时...
  • LSSY量化交易系统致力于逐步交易,不再主观交易,通过数据,做大概率,让量化交易变得更容易,大家都可以参与完善,以便更好的利于社区发展,目前采用邀请制,使用邀请编码能力完整的使用LSSY量化交易系统,提交...
  • vn.py项目起源于国内私募的自主交易系统,2015年初启动时只是单纯的交易API接口的Python封装。随着业内关注度的上升和社区不断的贡献,目前已经一步步成长为一套全面的交易程序开发框架,用户群体也日渐多样化,包括...
  • 期货交易系统的基本结构

    多人点赞 2020-12-11 10:52:55
    一套完整的 期货交易系统,除了要有交易功能,还要有分仓风控功能。 后台管理系统必须包含以下模块:系统设置、交易管理、账号管理、财务管理、柜员管理。 交易端包含PC交易端、安卓app、苹果app。其中app的止损止盈...
  • 后台sql server 界面采用c#设计。一套完整的数据库设计,包括论文
  • 一套大而全的系统架构体系与具体落地方案

    万次阅读 多人点赞 2018-01-10 13:23:08
    那个问题是:你们公司的IT系统架构是怎样的?又如何具体落地?采用了哪些开源或是商业的技术? 其实之前也写过或是做过一些关于系统架构的分享,或多或少的个人或其它限制,总觉得未能尽兴,留有遗憾。因此经过...
  • 何为“系统”,从system这一英语单词来看...真正的“器”首先是你在脑子里的知识,再来是你的交易系统的设计,构建,优化以及回测。事实上和任何一个发明一样,一个好的发明必然是一个完整,相互嵌合的一套系统。著名的
  • 他教给这13个学员一套完整交易系统,由于学员们被称为“海龟”,后来人们就把这套系统称为海龟交易系统。下面将介绍这套交易系统的具体实现。1.基本原理 原版的海龟交易系统包括市场(买什么),头寸规模(买卖...
  • 一套完整的港美股软件,有三大板块,1.开户端口2.行情端口.3.后台管理系统 开户端口:用手机号注册,获取验证码之后,填入验证码,即可注册开户,简单 2.行情端口展示图 3.后台管理系统展示图 数字化管理,券商...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,791
精华内容 10,316
关键字:

一套完整的交易系统