/*
* Copyright (c) [2016] [ <ether.camp> ]
* This file is part of the ethereumJ library.
*
* The ethereumJ library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ethereumJ library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.vm;
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
import org.ethereum.util.ByteUtil;
import java.math.BigInteger;
/**
* @author Roman Mandeleil
* @since 09.01.2015
*/
public class PrecompiledContracts {
private static final ECRecover ecRecover = new ECRecover();
private static final Sha256 sha256 = new Sha256();
private static final Ripempd160 ripempd160 = new Ripempd160();
private static final Identity identity = new Identity();
private static final DataWord ecRecoverAddr = new DataWord("0000000000000000000000000000000000000000000000000000000000000001");
private static final DataWord sha256Addr = new DataWord("0000000000000000000000000000000000000000000000000000000000000002");
private static final DataWord ripempd160Addr = new DataWord("0000000000000000000000000000000000000000000000000000000000000003");
private static final DataWord identityAddr = new DataWord("0000000000000000000000000000000000000000000000000000000000000004");
public static PrecompiledContract getContractForAddress(DataWord address) {
if (address == null) return identity;
if (address.equals(ecRecoverAddr)) return ecRecover;
if (address.equals(sha256Addr)) return sha256;
if (address.equals(ripempd160Addr)) return ripempd160;
if (address.equals(identityAddr)) return identity;
return null;
}
public static abstract class PrecompiledContract {
public abstract long getGasForData(byte[] data);
public abstract byte[] execute(byte[] data);
}
public static class Identity extends PrecompiledContract {
public Identity() {
}
@Override
public long getGasForData(byte[] data) {
// gas charge for the execution:
// minimum 1 and additional 1 for each 32 bytes word (round up)
if (data == null) return 15;
return 15 + (data.length + 31) / 32 * 3;
}
@Override
public byte[] execute(byte[] data) {
return data;
}
}
public static class Sha256 extends PrecompiledContract {
@Override
public long getGasForData(byte[] data) {
// gas charge for the execution:
// minimum 50 and additional 50 for each 32 bytes word (round up)
if (data == null) return 60;
return 60 + (data.length + 31) / 32 * 12;
}
@Override
public byte[] execute(byte[] data) {
if (data == null) return HashUtil.sha256(ByteUtil.EMPTY_BYTE_ARRAY);
return HashUtil.sha256(data);
}
}
public static class Ripempd160 extends PrecompiledContract {
@Override
public long getGasForData(byte[] data) {
// TODO #POC9 Replace magic numbers with constants
// gas charge for the execution:
// minimum 50 and additional 50 for each 32 bytes word (round up)
if (data == null) return 600;
return 600 + (data.length + 31) / 32 * 120;
}
@Override
public byte[] execute(byte[] data) {
byte[] result = null;
if (data == null) result = HashUtil.ripemd160(ByteUtil.EMPTY_BYTE_ARRAY);
else result = HashUtil.ripemd160(data);
return new DataWord(result).getData();
}
}
public static class ECRecover extends PrecompiledContract {
@Override
public long getGasForData(byte[] data) {
return 3000;
}
@Override
public byte[] execute(byte[] data) {
byte[] h = new byte[32];
byte[] v = new byte[32];
byte[] r = new byte[32];
byte[] s = new byte[32];
DataWord out = null;
try {
System.arraycopy(data, 0, h, 0, 32);
System.arraycopy(data, 32, v, 0, 32);
System.arraycopy(data, 64, r, 0, 32);
int sLength = data.length < 128 ? data.length - 96 : 32;
System.arraycopy(data, 96, s, 0, sLength);
ECKey.ECDSASignature signature = ECKey.ECDSASignature.fromComponents(r, s, v[31]);
if (validateV(v) && signature.validateComponents()) {
out = new DataWord(ECKey.signatureToAddress(h, signature));
}
} catch (Throwable any) {
}
if (out == null) {
return new byte[0];
} else {
return out.getData();
}
}
private static boolean validateV(byte[] v) {
for (int i = 0; i < v.length - 1; i++) {
if (v[i] != 0) return false;
}
return true;
}
}
}