以太坊多重钱包离线签名
pragma solidity ^0.4.23; contract MultiSign { uint public nonce; uint public threshold; // 满足threshold个签名即可转账 mapping (address => bool) owners; // 共同拥有人 address[] public ownersArr; // 共同拥有人地址 modifier isManager{ require( owners[msg.sender] == true); _; } //充值事件 event DepositFunds(address from, uint amount); //转账事件 event TransferFunds(address to, uint amount); constructor(uint threshold_, address[] owners_) public { require(owners_.length <= 10 && threshold_ <= owners_.length && threshold_ != 0); for (uint i=0; i<owners_.length; i++) { require(owners_[i] != address(0x0) && owners[owners_[i]] == false); owners[owners_[i]] = true; } ownersArr = owners_; threshold = threshold_; } //转账 function executeTransfer(uint8[] sigV, bytes32[] sigR, bytes32[] sigS, address destination, uint value, bytes data) isManager public{ require(sigR.length == threshold); require(sigR.length == sigS.length && sigR.length == sigV.length); bytes32 txHash = keccak256(byte(0x19), byte(0), address(this), destination, value, data, nonce); bytes memory prefix = "\x19Ethereum Signed Message:\n32"; for (uint i = 0; i < threshold; i++) { bytes32 prefixedHash = keccak256(prefix, txHash); address recovered = ecrecover(prefixedHash, sigV[i], sigR[i], sigS[i]); require(recovered != address(0x0) && owners[recovered] == true); } require(address(destination).call.value(value)(data)); nonce = nonce + 1; emit TransferFunds(destination, value); } function balanceOf() public view returns(uint){ return address(this).balance; } //充值 function () public payable{ emit DepositFunds(msg.sender, msg.value); } }
测试:
package com.osp.blockchain.eth.sign.test; import static org.web3j.tx.Contract.GAS_LIMIT; import static org.web3j.tx.ManagedTransaction.GAS_PRICE; import java.math.BigInteger; import java.util.Collections; import org.web3j.crypto.Credentials; import org.web3j.crypto.Hash; import org.web3j.crypto.Sign; import org.web3j.utils.Convert; import org.web3j.utils.Numeric; import com.osp.blockchain.eth.client.Web3JClient; import com.osp.blockchain.eth.transaction.ETHTransaction; import com.osp.blockchain.eth.wallet.ETHWallet; /** * 多重签名钱包交易测试 * * @author zmc * */ public class SignTest { // 0xc23a4fd38ba45f6e7d66aa9dc892fac683238c29 // 0xf7e5da402d15cf1f6c954bad4b402a58fac5a6fa public static final String RPCAddress = "http://10.20.31.114:8545/"; Credentials credentials = Credentials.create("f5b37f8daa631f49e64124b41a1f4768b6e755fc906781614024772710711cf0"); public MultiSign_sol_MultiSign multi = MultiSign_sol_MultiSign.load("0xf7e5da402d15cf1f6c954bad4b402a58fac5a6fa", Web3JClient.getClient(RPCAddress), credentials, GAS_PRICE, GAS_LIMIT); public static void main(String[] args) throws Exception { System.out.println(new SignTest().verifyAddress()); // new SignTest().createContract(); } /** * 创建合约 */ public void createContract() { String data = "0x608060405234801561001057600080fd5b5061042f806100206000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630a5606a21461005c5780631d972d41146100f65780636eceb96f14610151575b600080fd5b34801561006857600080fd5b506100b46004803603810190808035600019169060200190929190803560ff169060200190929190803560001916906020019092919080356000191690602001909291905050506101c3565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561010257600080fd5b50610137600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610303565b604051808215151515815260200191505060405180910390f35b34801561015d57600080fd5b506101a96004803603810190808035600019169060200190929190803560ff16906020019092919080356000191690602001909291908035600019169060200190929190505050610323565b604051808215151515815260200191505060405180910390f35b6000606060006040805190810160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250915081876040518083805190602001908083835b6020831015156102385780518252602082019150602081019050602083039250610213565b6001836020036101000a03801982511681845116808217855250505050505090500182600019166000191681526020019250505060405180910390209050600181878787604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af11580156102ed573d6000803e3d6000fd5b5050506020604051035192505050949350505050565b60006020528060005260406000206000915054906101000a900460ff1681565b600080600186868686604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af115801561039d573d6000803e3d6000fd5b5050506020604051035190506000808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff169150819150509493505050505600a165627a7a72305820284ccf723039373644f6d49849d7572140dd16e466af3fd4c40afe94c7195bc10029"; String accountAddress = "0x481a4f58ac488cd09e09321378c867999887f2af"; // 0x481a4f58ac488cd09e09321378c867999887f2af证书私钥 Credentials credentials = ETHWallet.getCredentials( new BigInteger("28048228501442048189267956591015494076577030861667140253009833017711730989442")); BigInteger nonce = ETHTransaction.ethGetTransactionCount(Web3JClient.getClient(RPCAddress), accountAddress); BigInteger value = Convert.toWei("0", Convert.Unit.ETHER).toBigInteger(); String txnHashString = ETHTransaction.createContractTransaction(Web3JClient.getClient(RPCAddress), credentials, nonce, GAS_PRICE, GAS_LIMIT, value, data); System.out.println("交易Hash====" + txnHashString); } public String verifyAddress() throws Exception { String hash = Hash.sha3String("https://steemit.com/@chaimyu"); Credentials credentials = Credentials .create("f5b37f8daa631f49e64124b41a1f4768b6e755fc906781614024772710711cf0"); System.out.println(credentials.getAddress()); byte[] data = Numeric.hexStringToByteArray(hash); Sign.SignatureData signature = Sign.signPrefixedMessage(data, credentials.getEcKeyPair()); System.out.println("R: " + Numeric.toHexString(signature.getR())); System.out.println("S: " + Numeric.toHexString(signature.getS())); System.out.println("V: " + Numeric.toBigInt(signature.getV())); byte[] myStringInByte = Numeric.hexStringToByteArray(hash); String dataString = this.multi .verify_addr(myStringInByte, Numeric.toBigInt(signature.getV()), signature.getR(), signature.getS()) .send(); return dataString; } // String to 64 length HexString (equivalent to 32 Hex lenght) public static String asciiToHex(String asciiValue) { char[] chars = asciiValue.toCharArray(); StringBuffer hex = new StringBuffer(); for (int i = 0; i < chars.length; i++) { hex.append(Integer.toHexString((int) chars[i])); } return hex.toString() + "".join("", Collections.nCopies(32 - (hex.length() / 2), "00")); } }