預編譯合約
預編譯合約是 EVM 中使用的一種折衷方案,用於提供更複雜的庫函數(通常用於復雜的操作,如加密、哈希等),不適合寫在操作碼中。 它們適用於簡單但經常調用的合約,或者邏輯固定但計算量大的合約。 預編譯合約是在客戶端使用客戶端代碼實現的,因為它們不需要 EVM,所以運行速度很快。 對於開發人員而言,與使用直接在 EVM 中運行的函數相比,它的成本也更低。
MAP 預編譯合約
為了簡化輕客戶端的開發,區塊鏈級別支持各種密碼學原語,並通過預編譯合約向 EVM 公開。
MAP 中繼鏈將實施預編譯合約以支持:
Ecrecover
地址 0x0000000000000000000000000000000000000001
ecrecover 作為原生合約實現。
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
}
sha256hash
地址 0x0000000000000000000000000000000000000002
SHA256 作為本地合約實施。
func (c *sha256hash) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
h := sha256.Sum256(input)
return h[:], nil
}
dataCopy
地址 0x0000000000000000000000000000000000000004
作為本機合約實現的數據副本。
func (c *dataCopy) Run(evm *EVM, contract *Contract, in []byte) ([]byte, error) {
return in, nil
}
bigModExp
地址 0x0000000000000000000000000000000000000005
bigModExp 實現原生大整數指數模運算。
func (c *bigModExp) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
var (
baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
modLen = new(big.Int).SetBytes(getData(input, 64, 32)).Uint64()
)
if len(input) > 96 {
input = input[96:]
} else {
input = input[:0]
}
// Handle a special case when both the base and mod length is zero
if baseLen == 0 && modLen == 0 {
return []byte{}, nil
}
// Retrieve the operands and execute the exponentiation
var (
base = new(big.Int).SetBytes(getData(input, 0, baseLen))
exp = new(big.Int).SetBytes(getData(input, baseLen, expLen))
mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen))
)
if mod.BitLen() == 0 {
// Modulo 0 is undefined, return zero
return common.LeftPadBytes([]byte{}, int(modLen)), nil
}
return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil
}
bn256AddIstanbul
地址 0x0000000000000000000000000000000000000006
bn256Add 實現了一個符合伊斯坦布爾共識規則的原生橢圓曲線點加法。
func (c *bn256AddIstanbul) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
return runBn256Add(input)
}
bn256ScalarMulIstanbul
地址 0x0000000000000000000000000000000000000007
bn256ScalarMuIstanbul 實現了一個符合伊斯坦布爾共識規則的原生橢圓曲線標量乘法。
func (c *bn256ScalarMulIstanbul) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
return runBn256ScalarMul(input)
}
bn256PairingIstanbul
Address 0x0000000000000000000000000000000000000008
bn256PairingIstanbul 實現bn256曲線的配對預編譯 符合伊斯坦布爾共識規則。
func (c *bn256PairingIstanbul) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
return runBn256Pairing(input)
}
儲存
Address 0x000068656164657273746F726541646472657373
執行 atlas header 儲存合約
func (s *store) Run(evm *EVM, contract *Contract, input []byte) (ret []byte, err error) {
return RunHeaderStore(evm, contract, input)
}
驗證
地址 0x0000000000747856657269667941646472657373
RunTxVerify execute atlas tx verify contract
func (tv *verify) Run(evm *EVM, contract *Contract, input []byte) (ret []byte, err error) {
return RunTxVerify(evm, contract, input)
}
轉移
地址 0x00000000000000000000000000000000000000fd
使 Atlas Gold ERC20 兼容的本地傳輸合約。
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
}
fractionMulExp
地址 0x00000000000000000000000000000000000000fc
計算 a * (b ^ exponent) 到精度的小數位,其中 a 和 b 是分數
func (c *fractionMulExp) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
// input is comprised of 6 arguments:
// aNumerator: 32 bytes, 256 bit integer, numerator for the first fraction (a)
// aDenominator: 32 bytes, 256 bit integer, denominator for the first fraction (a)
// bNumerator: 32 bytes, 256 bit integer, numerator for the second fraction (b)
// bDenominator: 32 bytes, 256 bit integer, denominator for the second fraction (b)
// exponent: 32 bytes, 256 bit integer, exponent to raise the second fraction (b) to
// decimals: 32 bytes, 256 bit integer, places of precision
//
// 6 args x 32 bytes each = 192 bytes total input length
if len(input) < 192 {
return nil, ErrInputLength
}
parseErrorStr := "Error parsing input: unable to parse %s value from %s"
aNumerator, parsed := math.ParseBig256(hexutil.Encode(input[0:32]))
if !parsed {
return nil, fmt.Errorf(parseErrorStr, "aNumerator", hexutil.Encode(input[0:32]))
}
aDenominator, parsed := math.ParseBig256(hexutil.Encode(input[32:64]))
if !parsed {
return nil, fmt.Errorf(parseErrorStr, "aDenominator", hexutil.Encode(input[32:64]))
}
bNumerator, parsed := math.ParseBig256(hexutil.Encode(input[64:96]))
if !parsed {
return nil, fmt.Errorf(parseErrorStr, "bNumerator", hexutil.Encode(input[64:96]))
}
bDenominator, parsed := math.ParseBig256(hexutil.Encode(input[96:128]))
if !parsed {
return nil, fmt.Errorf(parseErrorStr, "bDenominator", hexutil.Encode(input[96:128]))
}
exponent, parsed := math.ParseBig256(hexutil.Encode(input[128:160]))
if !parsed {
return nil, fmt.Errorf(parseErrorStr, "exponent", hexutil.Encode(input[128:160]))
}
decimals, parsed := math.ParseBig256(hexutil.Encode(input[160:192]))
if !parsed {
return nil, fmt.Errorf(parseErrorStr, "decimals", hexutil.Encode(input[160:192]))
}
// Handle passing of zero denominators
if aDenominator == big.NewInt(0) || bDenominator == big.NewInt(0) {
return nil, fmt.Errorf("Input Error: Denominator of zero provided!")
}
if !decimals.IsInt64() || !exponent.IsInt64() || max(decimals.Int64(), exponent.Int64()) > 100000 {
return nil, fmt.Errorf("Input Error: Decimals or exponent too large")
}
numeratorExp := new(big.Int).Mul(aNumerator, new(big.Int).Exp(bNumerator, exponent, nil))
denominatorExp := new(big.Int).Mul(aDenominator, new(big.Int).Exp(bDenominator, exponent, nil))
decimalAdjustment := new(big.Int).Exp(big.NewInt(10), decimals, nil) //10^18
numeratorDecimalAdjusted := new(big.Int).Div(new(big.Int).Mul(numeratorExp, decimalAdjustment), denominatorExp).Bytes()
denominatorDecimalAdjusted := decimalAdjustment.Bytes()
numeratorPadded := common.LeftPadBytes(numeratorDecimalAdjusted, 32)
denominatorPadded := common.LeftPadBytes(denominatorDecimalAdjusted, 32)
return append(numeratorPadded, denominatorPadded...), nil
}
佔有證明
地址 0x00000000000000000000000000000000000000fb
驗證validator的地址、publicKey、g1publickey、signature
func (c *proofOfPossession) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
// input is comprised of 3 arguments:
// address: 20 bytes, an address used to generate the proof-of-possession
// publicKey: 129 bytes, representing the public key (defined as a const in bls package)
// G1PUBLICKEYBYTES: 129 bytes, representing the bls public key (defined as a const in bls package)
// signature: 64 bytes, representing the signature on `address` (defined as a const in bls package)
// the total length of input required is the sum of these constants
if len(input) != common.AddressLength+blscrypto.PUBLICKEYBYTES+blscrypto.G1PUBLICKEYBYTES+blscrypto.SIGNATUREBYTES {
return nil, ErrInputLength
}
addressBytes := input[:common.AddressLength]
publicKeyBytes := input[common.AddressLength : common.AddressLength+blscrypto.PUBLICKEYBYTES]
publicKey, err := bls.UnmarshalPk(publicKeyBytes)
if err != nil {
return nil, err
}
apk := bls.NewApk(publicKey)
signatureBytes := input[common.AddressLength+blscrypto.PUBLICKEYBYTES+blscrypto.G1PUBLICKEYBYTES : common.AddressLength+blscrypto.PUBLICKEYBYTES+blscrypto.G1PUBLICKEYBYTES+blscrypto.SIGNATUREBYTES]
signature := bls.Signature{}
signature.Unmarshal(signatureBytes)
err = bls.Verify(apk, addressBytes, &signature)
if err != nil {
return nil, err
}
G1 := input[common.AddressLength+blscrypto.PUBLICKEYBYTES : common.AddressLength+blscrypto.PUBLICKEYBYTES+blscrypto.G1PUBLICKEYBYTES]
err = bls.VerifyG1Pk(G1, publicKeyBytes)
if err != nil {
return nil, err
}
return true32Byte, nil
}
getValidator 獲取驗證者
地址 0x00000000000000000000000000000000000000fa
返回簽署給定的、可能未密封的塊號所需的驗證器。 如果這個塊是一個紀元中的最後一個塊,請注意,這可能意味著這些驗證器中的一個或多個可能不再被選為後續塊。 警告:驗證器集總是從規範鏈構建,因此如果引擎知道總難度更高的鏈,則此預編譯是未定義的。
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
}
numberValidators 驗證者數量
地址 0x00000000000000000000000000000000000000f9
返回簽署此當前(可能未密封)塊所需的驗證者數量。 如果這個塊是一個紀元中的最後一個塊,請注意,這可能意味著這些驗證者中的一個或多個可能不再被選為後續塊。
警告:驗證者集總是從規範鏈構建,因此如果引擎知道總難度更高的鏈,則此預編譯是未定義的
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
}
epochSize 紀元大小
地址 0x00000000000000000000000000000000000000f8
返回epochSize
func (c *epochSize) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
epochSize := new(big.Int).SetUint64(evm.Context.EpochSize).Bytes()
epochSizeBytes := common.LeftPadBytes(epochSize[:], 32)
return epochSizeBytes, nil
}
blockNumberFromHeader
Address 0x00000000000000000000000000000000000000f7
返回blockNumber from header
func (c *blockNumberFromHeader) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
var header types.Header
err := rlp.DecodeBytes(input, &header)
if err != nil {
return nil, ErrInputDecode
}
blockNumber := header.Number.Bytes()
blockNumberBytes := common.LeftPadBytes(blockNumber[:], 32)
return blockNumberBytes, nil
}
hashHeader
Address 0x00000000000000000000000000000000000000f6
返回 the hashHeader from header
func (c *hashHeader) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
var header types.Header
err := rlp.DecodeBytes(input, &header)
if err != nil {
return nil, ErrInputDecode
}
hashBytes := header.Hash().Bytes()
return hashBytes, nil
}
getParentSealBitmap
Address 0x00000000000000000000000000000000000000F5
從鏈中過去區塊的父印章返回簽名者位圖signer bitmap。 請求的父印章必須在當前塊號的 4 個紀元epochs內發生。
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
}
getVerifiedSealBitmap
Address 0x00000000000000000000000000000000000000F4
返回 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
}
ed25519Verify
Address 0x00000000000000000000000000000000000000f3
ed25519Verify 實現了原生的 Ed25519 簽名驗證。
func (c *ed25519Verify) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
// Setup success/failure return values
var fail32byte, success32Byte = true32Byte, false32Byte
// Check if all required arguments are present
if len(input) < 96 {
return fail32byte, nil
}
publicKey := input[0:32] // 32 bytes
signature := input[32:96] // 64 bytes
message := input[96:] // arbitrary length
// Verify the Ed25519 signature against the public key and message
// https://godoc.org/golang.org/x/crypto/ed25519#Verify
if ed25519.Verify(publicKey, message, signature) {
return success32Byte, nil
}
return fail32byte, nil
}
BLS12_G1ADD
地址 0x000000000000000000000000000000000000000a
實現 EIP-2537 G1Add 預編譯。
> G1 加法調用需要“256”字節作為輸入,該輸入被解釋為兩個 G1 點(每個“128”字節)的字節串聯。
> 輸出是加法運算結果的編碼 - 單個 G1 點(128
字節)。
func (c *bls12381G1Add) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
if len(input) != 256 {
return nil, errBLS12381InvalidInputLength
}
var err error
var p0, p1 *bls12381.PointG1
// Initialize G1
g := bls12381.NewG1()
// Decode G1 point p_0
if p0, err = g.DecodePoint(input[:128]); err != nil {
return nil, err
}
// Decode G1 point p_1
if p1, err = g.DecodePoint(input[128:]); err != nil {
return nil, err
}
// Compute r = p_0 + p_1
r := g.New()
g.Add(r, p0, p1)
// Encode the G1 point result into 128 bytes
return g.EncodePoint(r), nil
}
BLS12_G1MUL
地址 0x000000000000000000000000000000000000000b
實現 EIP-2537 G1Mul 預編譯。
> G1 乘法調用需要“160”字節作為輸入,該輸入被解釋為 G1 點編碼(“128”字節)和標量值編碼(“32”字節)的字節串聯。
> 輸出是乘法運算結果的編碼 - 單個 G1 點(128
字節)。
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
}
BLS12_G1MULTIEXP
地址 0x000000000000000000000000000000000000000c
實現 EIP-2537 G1MultiExp 預編譯。
> G1 乘法調用期望“160*k”字節作為輸入,該輸入被解釋為“k”切片的字節串聯,每個切片都是 G1 點編碼(“128”字節)和標量值編碼(“ 32` 字節)。
> 輸出是多重指數運算結果的編碼 - 單個 G1 點(128
字節)。
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
}
BLS12_G2ADD
Address 0x000000000000000000000000000000000000000d
預編譯 EIP-2537 G2Add
> G2 加法調用需要“512”字節作為輸入,該輸入被解釋為兩個 G2 點(每個“256”字節)的字節串聯。
> 輸出是加法運算結果的編碼 - 單個 G2 點(256
字節)。
func (c *bls12381G2Add) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
if len(input) != 512 {
return nil, errBLS12381InvalidInputLength
}
var err error
var p0, p1 *bls12381.PointG2
// Initialize G2
g := bls12381.NewG2()
r := g.New()
// Decode G2 point p_0
if p0, err = g.DecodePoint(input[:256]); err != nil {
return nil, err
}
// Decode G2 point p_1
if p1, err = g.DecodePoint(input[256:]); err != nil {
return nil, err
}
// Compute r = p_0 + p_1
g.Add(r, p0, p1)
// Encode the G2 point into 256 bytes
return g.EncodePoint(r), nil
}
BLS12_G2MUL
Address 0x000000000000000000000000000000000000000e
預編譯 EIP-2537 G2MUL 邏輯.
> G2 乘法調用需要“288”字節作為輸入,該輸入被解釋為 G2 點編碼(“256”字節)和標量值編碼(“32”字節)的字節串聯。
> 輸出是乘法運算結果的編碼 - 單個 G2 點(256
字節)。
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
}
BLS12_G2MULTIEXP
Address 0x000000000000000000000000000000000000000f
預編譯 EIP-2537 G2MultiExp 邏輯
> G2 乘法調用期望將“288*k”字節作為輸入,該輸入被解釋為“k”切片的字節串聯,每個切片都是 G2 點(“256”字節)編碼和標量值編碼的字節串聯 (32
字節)。
> 輸出是多重指數運算結果的編碼 - 單個 G2 點(256
字節)。
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
}
BLS12_PAIRING
Address 0x0000000000000000000000000000000000000010
預編譯 EIP-2537 Pairing 邏輯.
> 配對調用需要 384*k
字節作為輸入,被解釋為 k
切片的字節連接。 每個切片具有以下結構:
> - 128
字節的 G1 點編碼
> - 256
字節的 G2 點編碼
> 輸出是一個“32”字節,如果配對結果等於配對目標字段中的乘法標識,最後一個字節為“0x01”,否則為“0x00”
>(分別相當於 Solidity 值 uint256(1)
和 uin256(0)
的 Big Endian 編碼)。
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
}
BLS12_MAP_FP_TO_G1
Address 0x0000000000000000000000000000000000000011
預變異 EIP-2537 Map_To_G1
> 字段到曲線調用需要“64”字節和一個被解釋為基本字段元素的輸入。
> 此調用的輸出是 128 個字節,並且是遵循各自編碼規則的 G1 點。
func (c *bls12381MapG1) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
if len(input) != 64 {
return nil, errBLS12381InvalidInputLength
}
// Decode input field element
fe, err := decodeBLS12381FieldElement(input)
if err != nil {
return nil, err
}
// Initialize G1
g := bls12381.NewG1()
// Compute mapping
r, err := g.MapToCurve(fe)
if err != nil {
return nil, err
}
// Encode the G1 point to 128 bytes
return g.EncodePoint(r), nil
}
BLS12_MAP_FP2_TO_G2
Address 0x0000000000000000000000000000000000000012
預編譯 EIP-2537 Map_FP2_TO_G2 邏輯
> 場到曲線調用需要“128”字節和一個輸入,該輸入被解釋為二次擴展場的一個元素。
> 此調用的輸出是 256
字節,並且是遵循各自編碼規則的 G2 點。
func (c *bls12381MapG2) Run(evm *EVM, contract *Contract, input []byte) ([]byte, error)
{
if len(input) != 128 {
return nil, errBLS12381InvalidInputLength
}
// Decode input field element
fe := make([]byte, 96)
c0, err := decodeBLS12381FieldElement(input[:64])
if err != nil
{
return nil, err
}
copy(fe[48:], c0)
c1, err := decodeBLS12381FieldElement(input[64:])
if err != nil
{
return nil, err
}
copy(fe[:48], c1)
// Initialize G2
g := bls12381.NewG2()
// Compute mapping
r, err := g.MapToCurve(fe)
if err != nil
{
return nil, err
}
// Encode the G2 point to 256 bytes
return g.EncodePoint(r), nil
}
Last updated
Was this helpful?