干货 | 编程小白模拟简易比特币系统,手把手带你写一波(附代码)
本文来源:区块链大本营
作者: VV一笑ヽ
如果有一个 p2p 的 demo,我们要怎么才能应用到区块链当中?
今天就来一起尝试一下吧!
首先,我们需要模拟网络中的多个节点相互通讯,我们假设现在的情况是有AB两个节点整个过程如下图所示:
梳理流程
让我们来梳理一下整个流程,明确在p2p网络中需要做的事情。
- 启动节点A。A首先创建一个创世区块
- 创建钱包A1。调用节点A提供的API创建一个钱包,此时A1的球球币为0。
- A1挖矿。调用节点A提供的挖矿API,生成新的区块,同时为A1的钱包有了系统奖励的球球币。
- 启动节点B。节点B要向A同步信息,当前的区块链,当前的交易池,当前的所有钱包的公钥。
- 创建钱包B1、A2,调用节点A和B的API,要广播(通知每一个节点)出去创建的钱包(公钥),目前节点只有两个,因此A需要告诉B,A2的钱包。B需要告诉A,B1的钱包。
- A1转账给B1。调用A提供的API,同时广播交易。
- A2挖矿记账。调用A提供的API,同时广播新生成的区块。
- 区块链信息
- 钱包信息
- 交易信息
- 发生新的交易
- 创建新的钱包
- 挖矿产生新区块
- client→server 发送消息,一般是请求数据
- server收到消息后,向client发送消息 (调用service,处理后返回数据)
- client收到消息处理数据(调用service,对数据处理)
相关代码
在实现的过程中,由于消息类型较多,封装了一个消息对象用来传输消息,对消息类型进行编码,统一处理,消息对象Message,实现了Serializable接口,使其对象可序列化:
public class Message implements Serializable { /** * 消息内容,就是我们的区块链、交易池等所需要的信息,使用JSON.toString转化到的json字符串 */ private String data; /** * 消息类型 */ private int type; }
/** * 查询最新的区块 */ private final static int QUERY_LATEST_BLOCK = 0; /** * 查询整个区块链 */ private final static int QUERY_BLOCK_CHAIN = 1; /** * 查询交易集合 */ private final static int QUERY_TRANSACTION = 2; /** * 查询已打包的交易集合 */ private final static int QUERY_PACKED_TRANSACTION = 3; /** * 查询钱包集合 */ private final static int QUERY_WALLET = 4; /** * 返回区块集合 */ private final static int RESPONSE_BLOCK_CHAIN = 5; /** * 返回交易集合 */ private final static int RESPONSE_TRANSACTION = 6; /** * 返回已打包交易集合 */ private final static int RESPONSE_PACKED_TRANSACTION = 7; /** * 返回钱包集合 */ private final static int RESPONSE_WALLET = 8;
1、client→server 发送消息,一般是请求数据
在client节点的启动类首先创建client对象,调用client内部方法,连接server。
启动类main方法中关键代码,(端口参数配置在args中):
P2PClient p2PClient = new P2PClient(); String url = "ws://localhost:"+args[0]+"/test"; p2PClient.connectToPeer(url);
public void connectToPeer(String url) throws IOException, DeploymentException { WebSocketContainer container = ContainerProvider.getWebSocketContainer(); URI uri = URI.create(url); this.session = container.connectToServer(P2PClient.class, uri); }
@OnOpen public void onOpen(Session session) { this.session = session; p2PService.sendMsg(session, p2PService.queryWalletMsg()); }
public String queryWalletMsg() { return JSON.toJSONString(new Message(QUERY_WALLET)); }
@Override public void sendMsg(Session session, String msg) { session.getAsyncRemote().sendText(msg); }
server收到消息,进入P2PServer中OnMessage方法
/** * 收到客户端发来消息 * @param msg 消息对象 */ @OnMessage public void onMessage(Session session, String msg) { p2PService.handleMessage(session, msg); }
@Override public void handleMessage(Session session, String msg) { Message message = JSON.parseObject(msg, Message.class); switch (message.getType()){ case QUERY_WALLET: sendMsg(session, responseWallets()); break; case RESPONSE_WALLET: handleWalletResponse(message.getData()); break; ...... }
private String responseWallets() { String wallets = blockService.findAllWallets(); return JSON.toJSONString(new Message(RESPONSE_WALLET, wallets)); }
@Override public String findAllWallets() { List wallets = new ArrayList<>(); myWalletMap.forEach((address, wallet) ->{ wallets.add(Wallet.builder().publicKey(wallet.getPublicKey()).build()); }); otherWalletMap.forEach((address, wallet) ->{ wallets.add(wallet); }); return JSON.toJSONString(wallets); }
因此我们的 responseWallets()方法中,最后一句话新建了一个message对象,并设置了信息码为RESPONSE_WALLET,在handleMessage中调用了sendmsg方法回传给client。
case QUERY_WALLET: sendMsg(session, responseWallets()); break;
client收到了请求得到的数据,进入P2PClient中的OnMessage方法
@OnMessage public void onMessage(String msg) { p2PService.handleMessage(this.session, msg); }
case RESPONSE_WALLET: handleWalletResponse(message.getData()); break;
private void handleWalletResponse(String msg) { List wallets = ""[]"".equals(msg)?new ArrayList<>():JSON.parseArray(msg, Wallet.class); wallets.forEach(wallet -> { blockService.addOtherWallet(walletService.getWalletAddress(wallet.getPublicKey()),wallet ); }); }
public class SpringUtil implements ApplicationContextAware { public static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (SpringUtil.applicationContext != null) { SpringUtil.applicationContext = applicationContext; } } /** * 获取applicationContext */ public static ApplicationContext getApplicationContext() { return applicationContext; }
/** * 通过name获取 Bean. */ public static Object getBean(String name) { return getApplicationContext().getBean(name); } /** * 通过class获取Bean. */ public static T getBean(Class clazz) { return getApplicationContext().getBean(clazz); }
/** * 通过name,以及Clazz返回指定的Bean */ public static T getBean(String name, Class clazz) { return getApplicationContext().getBean(name, clazz); } }
public static void main(String[] args) { System.out.println("Hello world"); SpringUtil.applicationContext = SpringApplication.run(Hello.class, args); if (args.length>0){ P2PClient p2PClient = new P2PClient(); String url = "ws://localhost:"+args[0]+"/test"; try { p2PClient.connectToPeer(url); } catch (Exception e) { e.printStackTrace(); } }
//之前是这样//@Autowired//private P2PService p2PService;//改正后,去掉Autowired,每次使用都手动获取beanprivate P2PService p2PService;@OnOpenpublic void onOpen(Session session) {//如果不使用那些,在这里会报空指针异常,p2PService 为 null p2PService = SpringUtil.getBean(P2PService.class);//新增这句话从IVO容器中获取bean p2PService.sendMsg(session, p2PService.queryWalletMsg());}
Test节点,测试时作为Client。
到此,我们就实现了p2p网络中server节点与client节点的交互过程。建议你也可以尝试一下,然后在评论区和我们讨论哦!
来源链接:https://www.8btc.com/media/567921
来源:巴比特
- 免责声明
- 世链财经作为开放的信息发布平台,所有资讯仅代表作者个人观点,与世链财经无关。如文章、图片、音频或视频出现侵权、违规及其他不当言论,请提供相关材料,发送到:2785592653@qq.com。
- 风险提示:本站所提供的资讯不代表任何投资暗示。投资有风险,入市须谨慎。
- 世链粉丝群:提供最新热点新闻,空投糖果、红包等福利,微信:juu3644。