区块链钱包开发(Android篇),Android程序员必会

布隆过滤器(Bloom Filter):过滤掉那些不包含有目标地址的交易信息,这一步能避免掉大量不相关的数据下载。

创建区块链

//创建区块链文件
File blockChainFile = new File(getDir(“blockstore”, Context.MODE_PRIVATE), “blockchain”);
//创建SPVBlockStore,管理区块数据
blockStore = new SPVBlockStore(Constants.NETWORK_PARAMETERS, blockChainFile);
//加载检查点
final InputStream checkpointsInputStream = getAssets().open(“checkpoints-testnet.txt”);
CheckpointManager.checkpoint(Constants.NETWORK_PARAMETERS, checkpointsInputStream,
blockStore, earliestKeyCreationTime);
//创建区块链对象
blockChain = new BlockChain(Constants.NETWORK_PARAMETERS, wallet, blockStore);

同步区块链

//添加网络权限:

private void startup() {
Log.d(TAG, "startup: ");
peerGroup = new PeerGroup(Constants.NETWORK_PARAMETERS, blockChain);
peerGroup.setDownloadTxDependencies(0); // recursive implementation causes StackOverflowError
peerGroup.addWallet(wallet);//设置钱包,重要
try {
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_ACTIVITIES);
peerGroup.setUserAgent(USER_AGENT, packageInfo.versionName);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
peerGroup.setMaxConnections(8);
int connectTimeout = (int) (15 * DateUtils.SECOND_IN_MILLIS);
peerGroup.setConnectTimeoutMillis(connectTimeout);
int discoveryTimeout = (int) (10 * DateUtils.SECOND_IN_MILLIS);
peerGroup.addConnectedEventListener(mPeerConnectedEventListener);
peerGroup.addDisconnectedEventListener(mPeerDisconnectedEventListener);
peerGroup.addDiscoveredEventListener(mPeerDiscoveredEventListener);
peerGroup.setPeerDiscoveryTimeoutMillis(discoveryTimeout);

//添加节点探索器,重要
peerGroup.addPeerDiscovery(new PeerDiscovery() {
private final PeerDiscovery normalPeerDiscovery = MultiplexingDiscovery
.forServices(Constants.NETWORK_PARAMETERS, 0);

@Override
public InetSocketAddress[] getPeers(final long services, final long timeoutValue,
final TimeUnit timeoutUnit) throws PeerDiscoveryException {
return normalPeerDiscovery.getPeers(services, timeoutValue, timeoutUnit);
}

@Override
public void shutdown() {
normalPeerDiscovery.shutdown();
}
});
peerGroup.startAsync();
peerGroup.startBlockChainDownload(null);
}

比特币收款

获取测试用比特币:testnet.manu.backend.hamburg/faucet 刚收到的币可能需要几分钟后才能使用

//监听比特币接受事件
wallet.addCoinsReceivedEventListener(mWalletListener);
//刷新余额
Coin balance = wallet.getBalance(Wallet.BalanceType.ESTIMATED);

比特币转账

比特币测试链转账查询 创建一个Tx,对Tx进行签名,对Tx进行P2P网络广播

Address address = Address.fromBase58(Constants.NETWORK_PARAMETERS, to);
//转账金额,以mBTC为单位
Coin coin = MonetaryFormat.MBTC.parse(amount);
//创建请求
SendRequest sendRequest = SendRequest.to(address, coin);
try {
//创建Transaction
Transaction transaction = wallet.sendCoinsOffline(sendRequest);
//通过P2P广播
BlockChainService.broadcastTransaction(BitcoinWalletActivity.this, transaction);
} catch (InsufficientMoneyException e) {
Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
e.printStackTrace();
}

public static void broadcastTransaction(Context context, Transaction transaction) {
Intent intent = new Intent(ACTION_BROADCAST_TRANSACTION, null, context, BlockChainService.class);
intent.putExtra(ACTION_BROADCAST_TRANSACTION_HASH, transaction.getHash().getBytes());
context.startService(intent);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
if (intent != null) {
byte[] txHash = intent.getByteArrayExtra(“tx”);
if (txHash != null) {
Sha256Hash sha256Hash = Sha256Hash.wrap(txHash);
Transaction transaction = BitcoinWalletManager.getInstance().getWallet().getTransaction(sha256Hash);
peerGroup.broadcastTransaction(transaction);
Log.d(TAG, "onStartCommand: " + sha256Hash.toString());
}
}
return super.onStartCommand(intent, flags, startId);
}

以太坊钱包

github.com/ethereumboo…

以太坊钱包功能与比特币钱包功能类似,获取用户余额,管理地址和密钥,转账、智能合约调用。以太坊钱包一般不用在本地维护区块链数据,只需要使用JSON-RPC访问

钱包文件

KeyStore = 私钥 + 密码

如果使用ImToken创建钱包,创建了助记词,密码用来加密钱包地址对应的子私钥,加密的结果就是Keystore.

{
“address”: “001d3f1ef827552ae1114027bd3ecf1f086ba0f9”,
“crypto”: {
“cipher”: “aes-128-ctr”,
“ciphertext”: “233a9f4d236ed0c13394b504b6da5df02587c8bf1ad8946f6f2b58f055507ece”,
“cipherparams”: {
“iv”: “d10c6ec5bae81b6cb9144de81037fa15”
},
“kdf”: “scrypt”,
“kdfparams”: {
“dklen”: 32,
“n”: 262144,
“p”: 1,
“r”: 8,
“salt”: “99d37a47c7c9429c66976f643f386a61b78b97f3246adca89abe4245d2788407”
},
“mac”: “594c8df1c8ee0ded8255a50caf07e8c12061fd859f4b7c76ab704b17c957e842”
},
“id”: “4fcb2ba4-ccdb-424f-89d5-26cce304bf9c”,
“version”: 3
}

以太坊钱包地址创建过程

1、使用Secp256k1创建公私钥

2、通过Keccak算法得到公钥Hash值,进而得到长度为40的地址字符串

3、一般的,会在地址字符串签名加前缀"0x"

Web3j创建钱包

Web3j

添加Web3j依赖

implementation ‘org.web3j:core:3.3.1-android’

创建新钱包

这里不涉及BIP协议,为非确定性钱包

Wallet.createStandard() 出现OOM, Out of Memory juejin.im/post/684490…

File walletDir = contextWrapper.getDir(“eth”, Context.MODE_PRIVATE);
//生成密钥对
ECKeyPair ecKeyPair = Keys.createEcKeyPair();
//WalletFile = KeyStore
WalletFile wallet = Wallet.createLight(PASSWORD, ecKeyPair);
String walletFileName = getWalletFileName(wallet);
File destination = new File(walletDir, walletFileName);
objectMapper.writeValue(destination, wallet);

加载钱包文件

File[] files = walletDir.listFiles();
wallet = objectMapper.readValue(files[0], WalletFile.class);

通过助记词创建钱包

涉及BIP协议,但没有遵循bitcoin地址只使用一次的原则,钱包一般只使用派生出来第一个地址

可通过工具检查派生的地址是否正确

//创建助记词
public List createMnemonics() throws MnemonicException.MnemonicLengthException {
SecureRandom secureRandom = new SecureRandom();
byte[] entropy = new byte[DeterministicSeed.DEFAULT_SEED_ENTROPY_BITS / 8];
secureRandom.nextBytes(entropy);
return MnemonicCode.INSTANCE.toMnemonic(entropy);
}

//m / 44’ / 60’ / 0’ / 0
//Hardened意思就是派生加固,防止获取到一个子私钥之后可以派生出后面的子私钥
//必须还有上一级的父私钥才能派生
public static final ImmutableList BIP44_ETH_ACCOUNT_ZERO_PATH =
ImmutableList.of(new ChildNumber(44, true), new ChildNumber(60, true),
ChildNumber.ZERO_HARDENED, ChildNumber.ZERO);

//通过助记词生成HD钱包
public void onCreateWallet(View view) {

byte[] seed = MnemonicCode.toSeed(words, “”);
DeterministicKey masterPrivateKey = HDKeyDerivation.createMasterPrivateKey(seed);
DeterministicHierarchy deterministicHierarchy = new DeterministicHierarchy(masterPrivateKey);
// m / 44’ / 60’ / 0’ / 0 / 0
DeterministicKey deterministicKey = deterministicHierarchy
.deriveChild(BIP44_ETH_ACCOUNT_ZERO_PATH, false, true, new ChildNumber(0));
byte[] bytes = deterministicKey.getPrivKeyBytes();
ECKeyPair keyPair = ECKeyPair.create(bytes);
try {
WalletFile walletFile = Wallet.createLight(PASSWORD, keyPair);
String address = walletFile.getAddress();
mAddress.setText(“0x” + address);
} catch (CipherException e) {
e.printStackTrace();
}
}

导出钱包

导出KeyStore

public String exportKeyStore(WalletFile wallet) {
try {
return objectMapper.writeValueAsString(wallet);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}

导出私钥

public String exportPrivateKey(WalletFile wallet) {
try {
ECKeyPair ecKeyPair = Wallet.decrypt(PASSWORD, wallet);
BigInteger privateKey = ecKeyPair.getPrivateKey();
return Numeric.toHexStringNoPrefixZeroPadded(privateKey, Keys.PRIVATE_KEY_LENGTH_IN_HEX);
} catch (CipherException e) {
e.printStackTrace();
}
return null;
}

导出助记词

一般可以将助记词加密存储,导出时解密。注意无法从KeyStore或者私钥导出助记词。

例如:使用IMToken导入私钥或者KeyStore创建的钱包,没有导出助记词的功能 如果是通过助记词创建的,就会有导出助记词的功能

ETH交易

获取Robsten测试网络ETH

查询余额

private Web3j mWeb3j = Web3jFactory.build(new HttpService(“https://ropsten.infura.io/1UoO4I/”));
BigInteger balance = mWeb3j.ethGetBalance(mAddress, DefaultBlockParameterName.LATEST).send().getBalance();
BigDecimal balance = Convert.fromWei(balance.toString(), Convert.Unit.ETHER);

ETH转账

转账记录查询

BigInteger transactionCount = mWeb3j.ethGetTransactionCount(mAddress, DefaultBlockParameterName.LATEST).send().getTransactionCount();
BigInteger gasPrice = mWeb3j.ethGasPrice().send().getGasPrice();
BigInteger gasLimit = new BigInteger(“200000”);
BigDecimal value = Convert.toWei(mAmountEdit.getText().toString().trim(), Convert.Unit.ETHER);
String to = mToAddressEdit.getText().toString().trim();
RawTransaction etherTransaction = RawTransaction.createEtherTransaction(transactionCount, gasPrice, gasLimit, to, value.toBigInteger());
ECKeyPair ecKeyPair = Wallet.decrypt(“a12345678”, mWalletFile);
Credentials credentials = Credentials.create(ecKeyPair);
byte[] bytes = TransactionEncoder.signMessage(etherTransaction, credentials);
String hexValue = Numeric.toHexString(bytes);
String transactionHash = mWeb3j.ethSendRawTransaction(hexValue).send().getTransactionHash();

Token交易

获取某Token余额

调用ERC20代币智能合约,获取当前地址的余额

//创建Function
private Function balanceOf(String owner) {
return new Function(“balanceOf”,
Collections.singletonList(new Address(owner)),
Collections.singletonList(new TypeReference(){}));
}

Function function = balanceOf(mAddress);
//调用智能合约
String s = callSmartContractFunction(function, CONTRACT_ADDRESS);
List decode = FunctionReturnDecoder.decode(s, function.getOutputParameters());
if (decode != null && decode.size() > 0) {
Uint256 type = (Uint256) decode.get(0);
BigInteger tokenBalance = type.getValue();
}

private String callSmartContractFunction(
Function function, String contractAddress) throws Exception {
String encodedFunction = FunctionEncoder.encode(function);

org.web3j.protocol.core.methods.response.EthCall response = mWeb3j.ethCall(
Transaction.createEthCallTransaction(
mAddress, contractAddress, encodedFunction),
DefaultBlockParameterName.LATEST)
.sendAsync().get();

return response.getValue();
}

Token转账

//创建Function
private Function transfer(String to, BigInteger value) {
return new Function(
“transfer”,
Arrays.asList(new Address(to), new Uint256(value)),
Collections.singletonList(new TypeReference() {}));
}

Function transfer = transfer(to, new BigInteger(amount));
//获取私钥,进行签名
ECKeyPair ecKeyPair = Wallet.decrypt(“a12345678”, mWalletFile);
Credentials credentials = Credentials.create(ecKeyPair);
String transactionHash = execute(credentials, transfer, CONTRACT_ADDRESS);

//执行合约调用
private String execute(
Credentials credentials, Function function, String contractAddress) throws Exception {
BigInteger nonce = mWeb3j.ethGetTransactionCount(mAddress, DefaultBlockParameterName.LATEST).send().getTransactionCount();
BigInteger gasPrice = mWeb3j.ethGasPrice().send().getGasPrice();
BigInteger gasLimit = new BigInteger(“200000”);
String encodedFunction = FunctionEncoder.encode(function);
RawTransaction rawTransaction = RawTransaction.createTransaction(
nonce,
gasPrice,
gasLimit,
contractAddress,
encodedFunction);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

学习福利

【Android 详细知识点思维脑图(技能树)】

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

详细整理在GitHub可以见;

Android架构视频+BAT面试专题PDF+学习笔记

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

*,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-W0kfAzJP-1711082470702)]

详细整理在GitHub可以见;

Android架构视频+BAT面试专题PDF+学习笔记

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值