Skip to content

Instantly share code, notes, and snippets.

@k06a
Last active January 18, 2021 14:51
Show Gist options
  • Save k06a/2760049a7eda31ee7ecfc4ed3e80554a to your computer and use it in GitHub Desktop.
Save k06a/2760049a7eda31ee7ecfc4ed3e80554a to your computer and use it in GitHub Desktop.
VanityPool
pragma solidity ^0.4.0;
contract VanityTask {
function lengthOfCommonPrefix(bytes a, bytes b) constant returns(uint) {
uint len = (a.length <= b.length) ? a.length : b.length;
for (uint i = 0; i < len; i++) {
if (a[i] != b[i]) {
return i;
}
}
return len;
}
function lengthOfCommonPrefix32(bytes32 a, bytes b) constant returns(uint) {
for (uint i = 0; i < b.length; i++) {
if (a[i] != b[i]) {
return i;
}
}
return b.length;
}
function equalBytesToBytes(bytes a, bytes b) constant returns (bool) {
if (a.length != b.length) {
return false;
}
for (uint i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
function equalBytes32ToBytes(bytes32 a, bytes b) constant returns (bool) {
for (uint i = 0; i < b.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
function bytesToBytes32(bytes source) constant returns (bytes32 result) {
assembly {
result := mload(add(source, 32))
}
}
/* Converts given number to base58, limited by 32 symbols */
function toBase58Checked(uint256 _value, byte appCode) constant returns (bytes32) {
string memory letters = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
bytes memory alphabet = bytes(letters);
uint8 base = 58;
uint8 len = 0;
uint256 remainder = 0;
bool needBreak = false;
bytes memory bytesReversed = bytes(new string(32));
for (uint8 i = 0; true; i++) {
if(_value < base){
needBreak = true;
}
remainder = _value % base;
_value = uint256(_value / base);
if (len == 32) {
for (uint j = 0; j < len - 1; j++) {
bytesReversed[j] = bytesReversed[j + 1];
}
len--;
}
bytesReversed[len] = alphabet[remainder];
len++;
if(needBreak){
break;
}
}
// Reverse
bytes memory result = bytes(new string(32));
result[0] = appCode;
for (i = 0; i < 31; i++) {
result[i + 1] = bytesReversed[len - 1 - i];
}
return bytesToBytes32(result);
}
function complexityForBtcAddressPrefix(bytes prefix, uint length) constant returns(uint) {
require(prefix.length >= length);
//TODO: Implement more complex algo
// https://bitcoin.stackexchange.com/questions/48586/best-way-to-calculate-difficulty-of-generating-specific-vanity-address
return 58 ** length;
}
// Create BTC Address: https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses#How_to_create_Bitcoin_Address
function createBtcAddressHex(bytes32 publicXPoint, bytes32 publicYPoint) constant returns(bytes32) {
bytes20 publicKeyPart = ripemd160(sha256(0x04, publicXPoint, publicYPoint));
bytes32 publicKeyCheckCode = sha256(sha256(0x00, publicKeyPart));
bytes memory publicKey = new bytes(32);
for (uint i = 0; i < 7; i++) {
publicKey[i] = 0x00;
}
publicKey[7] = 0x00; // Main Network
for (uint j = 0; j < 20; j++) {
publicKey[j + 8] = publicKeyPart[j];
}
publicKey[28] = publicKeyCheckCode[0];
publicKey[29] = publicKeyCheckCode[1];
publicKey[30] = publicKeyCheckCode[2];
publicKey[31] = publicKeyCheckCode[3];
return bytesToBytes32(publicKey);
}
function createBtcAddress(bytes32 publicXPoint, bytes32 publicYPoint) constant returns(bytes32) {
return toBase58Checked(uint256(createBtcAddressHex(publicXPoint, publicYPoint)), '1');
}
////////////////////////////////////////////////////////////////////////////
function test() constant returns(bool) {
return test_lengthOfCommonPrefix()
&& test_toBase58Checked()
&& test_createBtcAddress();
}
function testFail() constant returns(bool) {
require(false);
return true;
}
function test_lengthOfCommonPrefix() constant returns(bool) {
require(lengthOfCommonPrefix("123", "456") == 0);
require(lengthOfCommonPrefix("123", "4567") == 0);
require(lengthOfCommonPrefix("1234", "456") == 0);
require(lengthOfCommonPrefix("123", "156") == 1);
require(lengthOfCommonPrefix("123", "1567") == 1);
require(lengthOfCommonPrefix("1234", "156") == 1);
require(lengthOfCommonPrefix("123", "126") == 2);
require(lengthOfCommonPrefix("123", "1267") == 2);
require(lengthOfCommonPrefix("1234", "126") == 2);
require(lengthOfCommonPrefix("123", "123") == 3);
require(lengthOfCommonPrefix("123", "1237") == 3);
require(lengthOfCommonPrefix("1234", "123") == 3);
require(lengthOfCommonPrefix("123", "") == 0);
require(lengthOfCommonPrefix("", "1237") == 0);
return true;
}
function test_toBase58Checked() constant returns(bool) {
require(equalBytes32ToBytes(toBase58Checked(0x00010966776006953D5567439E5E39F86A0D273BEED61967F6, '1'), "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjv"));
return true;
}
function test_createBtcAddressHex() constant returns(bytes32) {
bytes32 xPoint = bytes32(0x50863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B2352);
bytes32 yPoint = bytes32(0x2CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6);
return createBtcAddressHex(xPoint, yPoint);
}
function test_createBtcAddress() constant returns(bool) {
bytes32 xPoint = bytes32(0x50863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B2352);
bytes32 yPoint = bytes32(0x2CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6);
require(equalBytes32ToBytes(createBtcAddress(xPoint, yPoint), "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjv"));
return true;
}
////////////////////////////////////////////////////////////////////////////
struct Answer {
// Fetch from miner
address miner;
bytes32 answerPublicXPoint;
bytes32 answerPublicYPoint;
bytes32 answerPrivateKey;
// Computed by contract
bytes32 addressPart;
uint complexity;
bool isFinalSolution;
}
address owner;
VanityPool pool;
bytes prefix;
bytes32 requestPublicXPoint;
bytes32 requestPublicYPoint;
uint minAllowedLengthOfCommonPrefixForReward;
uint complexity;
uint complexitySolved = 0;
bool foundSolution = false;
Answer[] answers;
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
modifier isValidBicoinAddressPrefix(bytes prefixArg) {
require(prefixArg.length >= 4);
require(prefixArg[0] == '1' || prefixArg[0] == '3');
for (uint i = 0; i < prefixArg.length; i++) {
byte ch = prefixArg[i];
require(ch != '0' &&
ch != 'O' &&
ch != 'I' &&
ch != 'l');
require((ch >= '1' && ch <= '9') ||
(ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z'));
}
_;
}
function VanityTask(address poolAddress,
bytes prefixArg,
bytes32 requestPublicXPointArg,
bytes32 requestPublicYPointArg)
isValidBicoinAddressPrefix(prefixArg) {
//address poolAddress = address(0x0);
//bytes memory prefixArg = "1Anton";
//bytes32 requestPublicXPointArg = hex"141511b7dc6c3b906d88d4f7acbbabdeeec6c7ab32be5eecc1b1e6c9a6e81f09";
//bytes32 requestPublicYPointArg = hex"2a26d9743e51ac77d5e8739ae507172c68cc57af727b2f57ab6e343765e476cd";
// hex"611b5ca5f79aefd448f728421e8b3329364fd86458418052e5e1023e74879ec7"
owner = msg.sender;
pool = VanityPool(poolAddress);
prefix = prefixArg;
requestPublicXPoint = requestPublicXPointArg;
requestPublicYPoint = requestPublicYPointArg;
minAllowedLengthOfCommonPrefixForReward = prefix.length - 2;
complexity = complexityForBtcAddressPrefix(prefix, prefix.length);
if (pool != address(0x0)) {
pool.registerTask();
}
}
function kill() onlyOwner() {
VanityPool vanityPool = pool;
selfdestruct(owner);
if (vanityPool != address(0x0)) {
vanityPool.removeTask();
}
}
function () payable {
if (pool != address(0x0)) {
pool.updateTask();
}
}
function redeem(uint amount) onlyOwner() {
owner.transfer(amount);
if (pool != address(0x0)) {
pool.updateTask();
}
}
// https://github.com/stonecoldpat/anonymousvoting/blob/master/LocalCrypto.sol
function invmod(uint a, uint p) internal constant returns (uint) {
if (a == 0 || a == p || p == 0)
return 0;
if (a > p)
a = a % p;
int t1;
int t2 = 1;
uint r1 = p;
uint r2 = a;
uint q;
while (r2 != 0) {
q = r1 / r2;
(t1, t2, r1, r2) = (t2, t1 - int(q) * t2, r2, r1 - q * r2);
}
if (t1 < 0)
return (p - uint(-t1));
return uint(t1);
}
// https://github.com/stonecoldpat/anonymousvoting/blob/master/LocalCrypto.sol
function submod(uint a, uint b, uint m) returns (uint){
uint a_nn;
if (a > b) {
a_nn = a;
} else {
a_nn = a + m;
}
return addmod(a_nn - b, 0, m);
}
// https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Point_addition
// https://github.com/bellaj/Blockchain/blob/6bffb47afae6a2a70903a26d215484cf8ff03859/ecdsa_bitcoin.pdf
// https://math.stackexchange.com/questions/2198139/elliptic-curve-formulas-for-point-addition
function addXY(uint x1, uint y1, uint x2, uint y2) returns(bytes32 x3, bytes32 y3) {
uint m = uint(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f);
uint anti = invmod(submod(x2, x1, m), m);
uint alpha = mulmod(submod(y2, y1, m), anti, m);
x3 = bytes32(submod(submod(mulmod(alpha, alpha, m), x2, m), x1, m));
y3 = bytes32(submod(mulmod(alpha, submod(x1, uint(x3), m), m), y1, m));
// x3 = bytes32(mul_mod(uint(x3), uint(y3), m)); == 1!!!!
// https://github.com/jbaylina/ecsol/blob/master/ec.sol
//(x3, y3) = ( bytes32(addmod( mulmod(y2, x1 , m) ,
// mulmod(x2, y1 , m),
// m)),
// bytes32(mulmod(y1, y2 , m))
// );
}
function postAnswerCheck(bytes32 answerPublicXPoint, bytes32 answerPublicYPoint) constant returns(uint) {
//bytes32 answerPublicXPoint = hex"bef8aa5dc83f75aff0e42f10b923e01e76d2f1d830e1a89dfe8621975a6226fe";
//bytes32 answerPublicYPoint = hex"6da72eaa3180aef4a0213e5430a6eb11a3b88f04914c7037e4bf1f499b11f8d5";
//bytes32 answerPrivateKey = bytes32(hex"eaf58f3ebd5ad92c16528b43ac41dadf267e50126568dcdd8a9196d469516659");
var (publicXPoint, publicYPoint) = addXY(uint(requestPublicXPoint), uint(requestPublicYPoint), uint(answerPublicXPoint), uint(answerPublicYPoint));
bytes32 btcAddress = createBtcAddress(publicXPoint, publicYPoint);
uint prefixLength = lengthOfCommonPrefix32(btcAddress, prefix);
return prefixLength;
}
function postAnswer(bytes32 answerPublicXPoint, bytes32 answerPublicYPoint, bytes32 answerPrivateKey) returns(bool) {
// Check private key generates exact same public key
// https://github.com/sontol/secp256k1evm
// require(TODO!)
var (publicXPoint, publicYPoint) = addXY(uint(requestPublicXPoint), uint(requestPublicYPoint), uint(answerPublicXPoint), uint(answerPublicYPoint));
bytes32 btcAddress = createBtcAddress(publicXPoint, publicYPoint);
uint prefixLength = lengthOfCommonPrefix32(btcAddress, prefix);
require(prefixLength >= minAllowedLengthOfCommonPrefixForReward);
Answer memory answer = Answer(
msg.sender,
answerPublicXPoint,
answerPublicYPoint,
answerPrivateKey,
btcAddress,
complexityForBtcAddressPrefix(prefix, prefixLength),
false);
if (prefixLength == prefix.length) {
// Final answer
answer.isFinalSolution = true;
foundSolution = true;
}
else if (prefixLength >= minAllowedLengthOfCommonPrefixForReward) {
// Good answer
answer.isFinalSolution = false;
}
else {
// Wrong answer
revert();
}
answers.push(answer);
uint amount = this.balance * answer.complexity / (complexity - complexitySolved);
if (amount > this.balance) {
amount = this.balance;
}
uint fee = 0;
if (pool != address(0x0)) {
fee = amount / 100;
amount -= fee;
}
msg.sender.transfer(amount); // TODO: Danger! Handle recursive call!
complexitySolved += answer.complexity;
if (pool != address(0x0)) {
pool.updateTask();
pool.transfer(fee);
}
return true;
}
}
contract VanityPool {
address owner;
VanityTask[] tasks;
event TaskRegistered(VanityTask task);
event TaskUpdated(VanityTask task);
event TaskRemoved();
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function isTaskRegistered(address task) returns(bool) {
for (uint i = 0; i < tasks.length; i++) {
if (tasks[i] == task) {
return true;
}
}
return false;
}
modifier onlyRegisteredTask(address task) {
require(isTaskRegistered(task));
_;
}
modifier onlyNotRegisteredTask(address task) {
require(!isTaskRegistered(task));
_;
}
function VanityPool() {
owner = msg.sender;
tasks = new VanityTask[](0);
}
function () payable {
}
function redeem(uint amount) onlyOwner() {
owner.transfer(amount);
}
function registerTask() onlyNotRegisteredTask(msg.sender) {
VanityTask task = VanityTask(msg.sender);
tasks.push(task);
TaskRegistered(task);
}
function updateTask() onlyRegisteredTask(msg.sender) {
VanityTask task = VanityTask(msg.sender);
TaskUpdated(task);
}
function removeTask() onlyRegisteredTask(msg.sender) {
VanityTask task = VanityTask(msg.sender);
for (uint i = 0; i < tasks.length; i++) {
if (tasks[i] == task) {
delete tasks[i];
for (uint j = i; j < tasks.length - 1; j++) {
tasks[j] = tasks[j + 1];
}
tasks.length--;
TaskRemoved();
break;
}
}
revert();
}
}
contract BitValid {
bytes32 constant mask4 = 0xffffffff00000000000000000000000000000000000000000000000000000000;
bytes1 constant networkConst = 0x00;
function getBitcoinAddress(
bytes32 _xPoint,
bytes32 _yPoint)
constant
returns(
bytes20 hashedPubKey,
bytes4 checkSum,
bytes1 network)
{
hashedPubKey = getHashedPublicKey(_xPoint, _yPoint);
checkSum = getCheckSum(hashedPubKey, networkConst);
network = networkConst;
}
function getHashedPublicKey(
bytes32 _xPoint,
bytes32 _yPoint)
constant
returns(
bytes20 hashedPubKey)
{
uint8 startingByte = 0x04;
return ripemd160(sha256(startingByte, _xPoint, _yPoint));
}
function getCheckSum(
bytes20 _hashedPubKey,
bytes1 network)
constant
returns(
bytes4 checkSum)
{
var full = sha256((sha256(network, _hashedPubKey)));
return bytes4(full & mask4);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment