/* * 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; } } }