Precompiled contracts are a compromise used in the EVM to provide more complex library functions (usually used for complex operations such as encryption, hashing, etc.) that are not suitable for writing in opcode. They are applied to contracts that are simple but frequently called, or that are logically fixed but computationally intensive. Precompiled contracts are implemented on the client-side with client code, and because they do not require the EVM, they run fast. It also costs less for developers than using functions that run directly in the EVM.
MAP Pre-compiled contracts
To ease the development of light clients, all kinds of cryptography primitives are supported at the blockchain level and are exposed to EVM via pre-compiled contracts.
MAP Relay Chain will implement the pre-compiled contracts to support:
func (c *ecrecover) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
const ecRecoverInputLength = 128
input = common.RightPadBytes(input, ecRecoverInputLength)
// "input" is (hash, v, r, s), each 32 bytes
// but for ecrecover we want (r, s, v)
r := new(big.Int).SetBytes(input[64:96])
s := new(big.Int).SetBytes(input[96:128])
v := input[63] - 27
// tighter sig s values input homestead only apply to tx sigs
if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) {
return nil, nil
}
// We must make sure not to modify the 'input', so placing the 'v' along with
// the signature needs to be done on a new allocation
sig := make([]byte, 65)
copy(sig, input[64:128])
sig[64] = v
// v needs to be at the end for libsecp256k1
pubKey, err := crypto.Ecrecover(input[:32], sig)
// make sure the public key is a valid one
if err != nil {
return nil, nil
}
// the first byte of pubkey is bitcoin heritage
return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32), nil
}
Native transfer contract to make Atlas Gold ERC20 compatible.
func (c *transfer) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
caller := contract.CallerAddress
atlasGoldAddress, err := evm.Context.GetRegisteredAddress(evm, params2.GoldTokenRegistryId)
if err != nil {
return nil, err
}
// input is comprised of 3 arguments:
// from: 32 bytes representing the address of the sender
// to: 32 bytes representing the address of the recipient
// value: 32 bytes, a 256 bit integer representing the amount of Atlas Gold to transfer
// 3 arguments x 32 bytes each = 96 bytes total input
if len(input) < 96 {
return nil, ErrInputLength
}
if caller != atlasGoldAddress {
return nil, fmt.Errorf("Unable to call transfer from unpermissioned address")
}
from := common.BytesToAddress(input[0:32])
to := common.BytesToAddress(input[32:64])
var parsed bool
value, parsed := math.ParseBig256(hexutil.Encode(input[64:96]))
if !parsed {
return nil, fmt.Errorf("Error parsing transfer: unable to parse value from " + hexutil.Encode(input[64:96]))
}
if from == params2.ZeroAddress {
// Mint case: Create cGLD out of thin air
evm.StateDB.AddBalance(to, value)
} else {
// Fail if we're trying to transfer more than the available balance
if !evm.Context.CanTransfer(evm.StateDB, from, value) {
return nil, ErrInsufficientBalance
}
//evm.Context.Transfer(evm, from, to, value)
}
return input, err
}
Return the validators that are required to sign the given, possibly unsealed, block number. If this block is the last in an epoch, note that that may mean one or more of those validators may no longer be elected for subsequent blocks. WARNING: Validator set is always constructed from the canonical chain, therefore this precompile is undefined if the engine is aware of a chain with higher total difficulty.
func (c *getValidator) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
// input is comprised of two arguments:
// index: 32 byte integer representing the index of the validator to get
// blockNumber: 32 byte integer representing the block number to access
if len(input) < 64 {
return nil, ErrInputLength
}
index := new(big.Int).SetBytes(input[0:32])
blockNumber := new(big.Int).SetBytes(input[32:64])
if blockNumber.Cmp(common.Big0) == 0 {
// Validator set for the genesis block is empty, so any index is out of bounds.
return nil, ErrValidatorsOutOfBounds
}
if blockNumber.Cmp(evm.Context.BlockNumber) > 0 {
return nil, ErrBlockNumberOutOfBounds
}
// Note: Passing empty hash as here as it is an extra expense and the hash is not actually used.
validators := evm.Context.GetValidators(new(big.Int).Sub(blockNumber, common.Big1), common.Hash{})
// Ensure index, which is guaranteed to be non-negative, is valid.
if index.Cmp(big.NewInt(int64(len(validators)))) >= 0 {
return nil, ErrValidatorsOutOfBounds
}
validatorAddress := validators[index.Uint64()].Address()
addressBytes := common.LeftPadBytes(validatorAddress[:], 32)
return addressBytes, nil
}
Return the number of validators that are required to sign this current, possibly unsealed, block. If this block is the last in an epoch, note that that may mean one or more of those validators may no longer be elected for subsequent blocks.
WARNING: Validator set is always constructed from the canonical chain,therefore this precompile is undefined if the engine is aware of a chain with higher total difficulty
func (c *numberValidators) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
// input is comprised of a single argument:
// blockNumber: 32 byte integer representing the block number to access
if len(input) < 32 {
return nil, ErrInputLength
}
blockNumber := new(big.Int).SetBytes(input[0:32])
if blockNumber.Cmp(common.Big0) == 0 {
// Genesis validator set is empty. Return 0.
return make([]byte, 32), nil
}
if blockNumber.Cmp(evm.Context.BlockNumber) > 0 {
return nil, ErrBlockNumberOutOfBounds
}
// Note: Passing empty hash as here as it is an extra expense and the hash is not actually used.
validators := evm.Context.GetValidators(new(big.Int).Sub(blockNumber, common.Big1), common.Hash{})
numberValidators := big.NewInt(int64(len(validators))).Bytes()
numberValidatorsBytes := common.LeftPadBytes(numberValidators[:], 32)
return numberValidatorsBytes, nil
}
Return the signer bitmap from the parent seal of a past block in the chain. Requested parent seal must have occurred within 4 epochs of the current block number.
func (c *getParentSealBitmap) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
// input is comprised of a single argument:
// blockNumber: 32 byte integer representing the block number to access
if len(input) < 32 {
return nil, ErrInputLength
}
blockNumber := new(big.Int).SetBytes(input[0:32])
// Ensure the request is for information from a previously sealed block.
if blockNumber.Cmp(common.Big0) == 0 || blockNumber.Cmp(evm.Context.BlockNumber) > 0 {
return nil, ErrBlockNumberOutOfBounds
}
// Ensure the request is for a sufficiently recent block to limit state expansion.
historyLimit := new(big.Int).SetUint64(evm.Context.EpochSize * 4)
if blockNumber.Cmp(new(big.Int).Sub(evm.Context.BlockNumber, historyLimit)) <= 0 {
return nil, ErrBlockNumberOutOfBounds
}
header := evm.Context.GetHeaderByNumber(blockNumber.Uint64())
if header == nil {
log.Error("Unexpected failure to retrieve block in getParentSealBitmap precompile", "blockNumber", blockNumber)
return nil, ErrUnexpected
}
extra, err := types.ExtractIstanbulExtra(header)
if err != nil {
log.Error("Header without Istanbul extra data encountered in getParentSealBitmap precompile", "blockNumber", blockNumber, "err", err)
return nil, ErrEngineIncompatible
}
return common.LeftPadBytes(extra.ParentAggregatedSeal.Bitmap.Bytes()[:], 32), nil
}
rerurn the extra.AggregatedSeal.Bitmap from header
func (c *getVerifiedSealBitmap) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
// input is comprised of a single argument:
// header: rlp encoded block header
var header types.Header
if err := rlp.DecodeBytes(input, &header); err != nil {
return nil, ErrInputDecode
}
// Verify the seal against the engine rules.
if !evm.Context.VerifySeal(&header) {
return nil, ErrInputVerification
}
// Extract the verified seal from the header.
extra, err := types.ExtractIstanbulExtra(&header)
if err != nil {
log.Error("Header without Istanbul extra data encountered in getVerifiedSealBitmap precompile", "extraData", header.Extra, "err", err)
// Seal verified by a non-Istanbul engine. Return an error.
return nil, ErrEngineIncompatible
}
return common.LeftPadBytes(extra.AggregatedSeal.Bitmap.Bytes()[:], 32), nil
}
> G1 multiplication call expects 160 bytes as an input that is interpreted as byte concatenation of encoding of G1 point (128 bytes) and encoding of a scalar value (32 bytes).
> Output is an encoding of multiplication operation result - single G1 point(128 bytes).
func (c *bls12381G1Mul) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
if len(input) != 160 {
return nil, errBLS12381InvalidInputLength
}
var err error
var p0 *bls12381.PointG1
// Initialize G1
g := bls12381.NewG1()
// Decode G1 point
if p0, err = g.DecodePoint(input[:128]); err != nil {
return nil, err
}
// Decode scalar value
e := new(big.Int).SetBytes(input[128:])
// Compute r = e * p_0
r := g.New()
g.MulScalar(r, p0, e)
// Encode the G1 point into 128 bytes
return g.EncodePoint(r), nil
}
G1 multiplication call expects 160*k bytes as an input that is interpreted as byte concatenation of k slices each of them being a byte concatenation of encoding of G1 point (128 bytes) and encoding of a scalar value (32 bytes).
Output is an encoding of multiexponentiation operation result - single G1 point (128 bytes).
func (c *bls12381G1MultiExp) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
k := len(input) / 160
if len(input) == 0 || len(input)%160 != 0 {
return nil, errBLS12381InvalidInputLength
}
var err error
points := make([]*bls12381.PointG1, k)
scalars := make([]*big.Int, k)
// Initialize G1
g := bls12381.NewG1()
// Decode point scalar pairs
for i := 0; i < k; i++ {
off := 160 * i
t0, t1, t2 := off, off+128, off+160
// Decode G1 point
if points[i], err = g.DecodePoint(input[t0:t1]); err != nil {
return nil, err
}
// Decode scalar value
scalars[i] = new(big.Int).SetBytes(input[t1:t2])
}
// Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
r := g.New()
g.MultiExp(r, points, scalars)
// Encode the G1 point to 128 bytes
return g.EncodePoint(r), nil
}
> G2 multiplication call expects 288 bytes as an input that is interpreted as byte concatenation of encoding of G2 point (256 bytes) and encoding of a scalar value (32 bytes).
> Output is an encoding of multiplication operation result - single G2 point (256 bytes).
func (c *bls12381G2Mul) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
if len(input) != 288 {
return nil, errBLS12381InvalidInputLength
}
var err error
var p0 *bls12381.PointG2
// Initialize G2
g := bls12381.NewG2()
// Decode G2 point
if p0, err = g.DecodePoint(input[:256]); err != nil {
return nil, err
}
// Decode scalar value
e := new(big.Int).SetBytes(input[256:])
// Compute r = e * p_0
r := g.New()
g.MulScalar(r, p0, e)
// Encode the G2 point into 256 bytes
return g.EncodePoint(r), nil
}
> G2 multiplication call expects 288*k bytes as an input that is interpreted as byte concatenation of k slices each of them being a byte concatenation of encoding of G2 point (256 bytes) and encoding of a scalar value (32 bytes).
> Output is an encoding of multiexponentiation operation result - single G2 point (256 bytes).
func (c *bls12381G2MultiExp) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
k := len(input) / 288
if len(input) == 0 || len(input)%288 != 0 {
return nil, errBLS12381InvalidInputLength
}
var err error
points := make([]*bls12381.PointG2, k)
scalars := make([]*big.Int, k)
// Initialize G2
g := bls12381.NewG2()
// Decode point scalar pairs
for i := 0; i < k; i++ {
off := 288 * i
t0, t1, t2 := off, off+256, off+288
// Decode G1 point
if points[i], err = g.DecodePoint(input[t0:t1]); err != nil {
return nil, err
}
// Decode scalar value
scalars[i] = new(big.Int).SetBytes(input[t1:t2])
}
// Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
r := g.New()
g.MultiExp(r, points, scalars)
// Encode the G2 point to 256 bytes.
return g.EncodePoint(r), nil
}
> Pairing call expects 384*k bytes as an inputs that is interpreted as byte concatenation of k slices. Each slice has the following structure:
> - 128 bytes of G1 point encoding
> - 256 bytes of G2 point encoding
> Output is a 32 bytes where last single byte is 0x01 if pairing result is equal to multiplicative identity in a pairing target field and 0x00 otherwise
> (which is equivalent of Big Endian encoding of Solidity values uint256(1) and uin256(0) respectively).
func (c *bls12381Pairing) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
k := len(input) / 384
if len(input) == 0 || len(input)%384 != 0 {
return nil, errBLS12381InvalidInputLength
}
// Initialize BLS12-381 pairing engine
e := bls12381.NewPairingEngine()
g1, g2 := e.G1, e.G2
// Decode pairs
for i := 0; i < k; i++ {
off := 384 * i
t0, t1, t2 := off, off+128, off+384
// Decode G1 point
p1, err := g1.DecodePoint(input[t0:t1])
if err != nil {
return nil, err
}
// Decode G2 point
p2, err := g2.DecodePoint(input[t1:t2])
if err != nil {
return nil, err
}
// 'point is on curve' check already done,
// Here we need to apply subgroup checks.
if !g1.InCorrectSubgroup(p1) {
return nil, errBLS12381G1PointSubgroup
}
if !g2.InCorrectSubgroup(p2) {
return nil, errBLS12381G2PointSubgroup
}
// Update pairing engine with G1 and G2 ponits
e.AddPair(p1, p2)
}
// Prepare 32 byte output
out := make([]byte, 32)
// Compute pairing and set the result
if e.Check() {
out[31] = 1
}
return out, nil
}