forked from 33cn/plugin
/
contracts.go
248 lines (217 loc) · 8.09 KB
/
contracts.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import (
"crypto/sha256"
"math"
"math/big"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common/crypto"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/params"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/model"
"golang.org/x/crypto/ripemd160"
)
// PrecompiledContract 系统内置合约实现的接口,只包含两个操作:
// 1 根据合约自身逻辑和入参,计算所需Gas;
// 2 执行合约。
type PrecompiledContract interface {
// 计算当前合约执行需要消耗的Gas
RequiredGas(input []byte) uint64
// 执行预编译的合约固定逻辑,input为入参
Run(input []byte) ([]byte, error)
}
// PrecompiledContractsByzantium chain33平台支持君士坦丁堡版本支持的所有预编译合约指令,并从此版本开始同步支持EVM黄皮书中的新增指令;
// 保存拜占庭版本支持的所有预编译合约(包括之前版本的合约);
// 后面如果有硬分叉,需要在此处考虑分叉逻辑,根据区块高度分别处理;
// 下面的8个预编译指令,直接引用go-ethereum中的EVM实现
var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{},
common.BytesToAddress([]byte{6}): &bn256Add{},
common.BytesToAddress([]byte{7}): &bn256ScalarMul{},
// FIXME 这里还需要依赖EVM包装过的bn256包(common.crypto.bn256),没法使用原生bn256,后继需要优化为使用原生bn256
// 难度比较大,优先级放低
common.BytesToAddress([]byte{8}): &bn256Pairing{},
}
// RunPrecompiledContract 调用预编译的合约逻辑并返回结果
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.RequiredGas(input)
if contract.UseGas(gas) {
return p.Run(input)
}
return nil, model.ErrOutOfGas
}
// 预编译合约 ECRECOVER 椭圆曲线算法支持
type ecrecover struct{}
// RequiredGas 需要消耗多少Gas
func (c *ecrecover) RequiredGas(input []byte) uint64 {
return params.EcrecoverGas
}
// Run 运算
func (c *ecrecover) Run(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 !common.AllZero(input[32:63]) || !crypto.ValidateSignatureValues(r, s) {
return nil, nil
}
// v needs to be at the end for libsecp256k1
pubKey, err := crypto.Ecrecover(input[:32], append(input[64:128], v))
// 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
}
// SHA256 implemented as a native contract.
type sha256hash struct{}
// RequiredGas Returns the gas required to Execute the pre-compiled contract.
//
// This method does not require any overflow checking as the input size gas costs
// required for anything significant is so high it's impossible to pay for.
func (c *sha256hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
}
// Run run
func (c *sha256hash) Run(input []byte) ([]byte, error) {
h := sha256.Sum256(input)
return h[:], nil
}
// RIPMED160 implemented as a native contract.
type ripemd160hash struct{}
// RequiredGas Returns the gas required to Execute the pre-compiled contract.
//
// This method does not require any overflow checking as the input size gas costs
// required for anything significant is so high it's impossible to pay for.
func (c *ripemd160hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
}
// Run run
func (c *ripemd160hash) Run(input []byte) ([]byte, error) {
ripemd := ripemd160.New()
ripemd.Write(input)
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
}
// data copy implemented as a native contract.
type dataCopy struct{}
// RequiredGas Returns the gas required to Execute the pre-compiled contract.
//
// This method does not require any overflow checking as the input size gas costs
// required for anything significant is so high it's impossible to pay for.
func (c *dataCopy) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
}
// Run run
func (c *dataCopy) Run(in []byte) ([]byte, error) {
return in, nil
}
// bigModExp implements a native big integer exponential modular Operation.
type bigModExp struct{}
var (
big1 = big.NewInt(1)
big4 = big.NewInt(4)
big8 = big.NewInt(8)
big16 = big.NewInt(16)
big32 = big.NewInt(32)
big64 = big.NewInt(64)
big96 = big.NewInt(96)
big480 = big.NewInt(480)
big1024 = big.NewInt(1024)
big3072 = big.NewInt(3072)
big199680 = big.NewInt(199680)
)
// RequiredGas Returns the gas required to Execute the pre-compiled contract.
func (c *bigModExp) RequiredGas(input []byte) uint64 {
var (
baseLen = new(big.Int).SetBytes(common.GetData(input, 0, 32))
expLen = new(big.Int).SetBytes(common.GetData(input, 32, 32))
modLen = new(big.Int).SetBytes(common.GetData(input, 64, 32))
)
if len(input) > 96 {
input = input[96:]
} else {
input = input[:0]
}
// Retrieve the head 32 bytes of exp for the adjusted exponent length
var expHead *big.Int
if big.NewInt(int64(len(input))).Cmp(baseLen) <= 0 {
expHead = new(big.Int)
} else {
if expLen.Cmp(big32) > 0 {
expHead = new(big.Int).SetBytes(common.GetData(input, baseLen.Uint64(), 32))
} else {
expHead = new(big.Int).SetBytes(common.GetData(input, baseLen.Uint64(), expLen.Uint64()))
}
}
// Calculate the adjusted exponent length
var msb int
if bitlen := expHead.BitLen(); bitlen > 0 {
msb = bitlen - 1
}
adjExpLen := new(big.Int)
if expLen.Cmp(big32) > 0 {
adjExpLen.Sub(expLen, big32)
adjExpLen.Mul(big8, adjExpLen)
}
adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))
// Calculate the gas cost of the Operation
gas := new(big.Int).Set(common.BigMax(modLen, baseLen))
switch {
case gas.Cmp(big64) <= 0:
gas.Mul(gas, gas)
case gas.Cmp(big1024) <= 0:
gas = new(big.Int).Add(
new(big.Int).Div(new(big.Int).Mul(gas, gas), big4),
new(big.Int).Sub(new(big.Int).Mul(big96, gas), big3072),
)
default:
gas = new(big.Int).Add(
new(big.Int).Div(new(big.Int).Mul(gas, gas), big16),
new(big.Int).Sub(new(big.Int).Mul(big480, gas), big199680),
)
}
gas.Mul(gas, common.BigMax(adjExpLen, big1))
gas.Div(gas, new(big.Int).SetUint64(params.ModExpQuadCoeffDiv))
if gas.BitLen() > 64 {
return math.MaxUint64
}
return gas.Uint64()
}
// Run run
func (c *bigModExp) Run(input []byte) ([]byte, error) {
var (
baseLen = new(big.Int).SetBytes(common.GetData(input, 0, 32)).Uint64()
expLen = new(big.Int).SetBytes(common.GetData(input, 32, 32)).Uint64()
modLen = new(big.Int).SetBytes(common.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(common.GetData(input, 0, baseLen))
exp = new(big.Int).SetBytes(common.GetData(input, baseLen, expLen))
mod = new(big.Int).SetBytes(common.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
}