oracle_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. // Copyright 2019 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package checkpointoracle
  17. import (
  18. "bytes"
  19. "crypto/ecdsa"
  20. "encoding/binary"
  21. "errors"
  22. "math/big"
  23. "reflect"
  24. "sort"
  25. "testing"
  26. "time"
  27. "github.com/ethereum/go-ethereum/accounts/abi/bind"
  28. "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
  29. "github.com/ethereum/go-ethereum/common"
  30. "github.com/ethereum/go-ethereum/contracts/checkpointoracle/contract"
  31. "github.com/ethereum/go-ethereum/core"
  32. "github.com/ethereum/go-ethereum/crypto"
  33. "github.com/ethereum/go-ethereum/params"
  34. )
  35. var (
  36. emptyHash = [32]byte{}
  37. checkpoint0 = params.TrustedCheckpoint{
  38. SectionIndex: 0,
  39. SectionHead: common.HexToHash("0x7fa3c32f996c2bfb41a1a65b3d8ea3e0a33a1674cde43678ad6f4235e764d17d"),
  40. CHTRoot: common.HexToHash("0x98fc5d3de23a0fecebad236f6655533c157d26a1aedcd0852a514dc1169e6350"),
  41. BloomRoot: common.HexToHash("0x99b5adb52b337fe25e74c1c6d3835b896bd638611b3aebddb2317cce27a3f9fa"),
  42. }
  43. checkpoint1 = params.TrustedCheckpoint{
  44. SectionIndex: 1,
  45. SectionHead: common.HexToHash("0x2d4dee68102125e59b0cc61b176bd89f0d12b3b91cfaf52ef8c2c82fb920c2d2"),
  46. CHTRoot: common.HexToHash("0x7d428008ece3b4c4ef5439f071930aad0bb75108d381308df73beadcd01ded95"),
  47. BloomRoot: common.HexToHash("0x652571f7736de17e7bbb427ac881474da684c6988a88bf51b10cca9a2ee148f4"),
  48. }
  49. checkpoint2 = params.TrustedCheckpoint{
  50. SectionIndex: 2,
  51. SectionHead: common.HexToHash("0x61c0de578c0115b1dff8ef39aa600588c7c6ecb8a2f102003d7cf4c4146e9291"),
  52. CHTRoot: common.HexToHash("0x407a08a407a2bc3838b74ca3eb206903c9c8a186ccf5ef14af07794efff1970b"),
  53. BloomRoot: common.HexToHash("0x058b4161f558ce295a92925efc57f34f9210d5a30088d7475c183e0d3e58f5ac"),
  54. }
  55. )
  56. var (
  57. // The block frequency for creating checkpoint(only used in test)
  58. sectionSize = big.NewInt(512)
  59. // The number of confirmations needed to generate a checkpoint(only used in test).
  60. processConfirms = big.NewInt(4)
  61. )
  62. // validateOperation executes the operation, watches and delivers all events fired by the backend and ensures the
  63. // correctness by assert function.
  64. func validateOperation(t *testing.T, c *contract.CheckpointOracle, backend *backends.SimulatedBackend, operation func(),
  65. assert func(<-chan *contract.CheckpointOracleNewCheckpointVote) error, opName string) {
  66. // Watch all events and deliver them to assert function
  67. var (
  68. sink = make(chan *contract.CheckpointOracleNewCheckpointVote)
  69. sub, _ = c.WatchNewCheckpointVote(nil, sink, nil)
  70. )
  71. defer func() {
  72. // Close all subscribers
  73. sub.Unsubscribe()
  74. }()
  75. operation()
  76. // flush pending block
  77. backend.Commit()
  78. if err := assert(sink); err != nil {
  79. t.Errorf("operation {%s} failed, err %s", opName, err)
  80. }
  81. }
  82. // validateEvents checks that the correct number of contract events
  83. // fired by contract backend.
  84. func validateEvents(target int, sink interface{}) (bool, []reflect.Value) {
  85. chanval := reflect.ValueOf(sink)
  86. chantyp := chanval.Type()
  87. if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.RecvDir == 0 {
  88. return false, nil
  89. }
  90. count := 0
  91. var recv []reflect.Value
  92. timeout := time.After(1 * time.Second)
  93. cases := []reflect.SelectCase{{Chan: chanval, Dir: reflect.SelectRecv}, {Chan: reflect.ValueOf(timeout), Dir: reflect.SelectRecv}}
  94. for {
  95. chose, v, _ := reflect.Select(cases)
  96. if chose == 1 {
  97. // Not enough event received
  98. return false, nil
  99. }
  100. count += 1
  101. recv = append(recv, v)
  102. if count == target {
  103. break
  104. }
  105. }
  106. done := time.After(50 * time.Millisecond)
  107. cases = cases[:1]
  108. cases = append(cases, reflect.SelectCase{Chan: reflect.ValueOf(done), Dir: reflect.SelectRecv})
  109. chose, _, _ := reflect.Select(cases)
  110. // If chose equal 0, it means receiving redundant events.
  111. return chose == 1, recv
  112. }
  113. func signCheckpoint(addr common.Address, privateKey *ecdsa.PrivateKey, index uint64, hash common.Hash) []byte {
  114. // EIP 191 style signatures
  115. //
  116. // Arguments when calculating hash to validate
  117. // 1: byte(0x19) - the initial 0x19 byte
  118. // 2: byte(0) - the version byte (data with intended validator)
  119. // 3: this - the validator address
  120. // -- Application specific data
  121. // 4 : checkpoint section_index(uint64)
  122. // 5 : checkpoint hash (bytes32)
  123. // hash = keccak256(checkpoint_index, section_head, cht_root, bloom_root)
  124. buf := make([]byte, 8)
  125. binary.BigEndian.PutUint64(buf, index)
  126. data := append([]byte{0x19, 0x00}, append(addr.Bytes(), append(buf, hash.Bytes()...)...)...)
  127. sig, _ := crypto.Sign(crypto.Keccak256(data), privateKey)
  128. sig[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
  129. return sig
  130. }
  131. // assertSignature verifies whether the recovered signers are equal with expected.
  132. func assertSignature(addr common.Address, index uint64, hash [32]byte, r, s [32]byte, v uint8, expect common.Address) bool {
  133. buf := make([]byte, 8)
  134. binary.BigEndian.PutUint64(buf, index)
  135. data := append([]byte{0x19, 0x00}, append(addr.Bytes(), append(buf, hash[:]...)...)...)
  136. pubkey, err := crypto.Ecrecover(crypto.Keccak256(data), append(r[:], append(s[:], v-27)...))
  137. if err != nil {
  138. return false
  139. }
  140. var signer common.Address
  141. copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
  142. return bytes.Equal(signer.Bytes(), expect.Bytes())
  143. }
  144. type Account struct {
  145. key *ecdsa.PrivateKey
  146. addr common.Address
  147. }
  148. type Accounts []Account
  149. func (a Accounts) Len() int { return len(a) }
  150. func (a Accounts) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  151. func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 }
  152. func TestCheckpointRegister(t *testing.T) {
  153. // Initialize test accounts
  154. var accounts Accounts
  155. for i := 0; i < 3; i++ {
  156. key, _ := crypto.GenerateKey()
  157. addr := crypto.PubkeyToAddress(key.PublicKey)
  158. accounts = append(accounts, Account{key: key, addr: addr})
  159. }
  160. sort.Sort(accounts)
  161. // Deploy registrar contract
  162. contractBackend := backends.NewSimulatedBackend(
  163. core.GenesisAlloc{
  164. accounts[0].addr: {Balance: big.NewInt(10000000000000000)},
  165. accounts[1].addr: {Balance: big.NewInt(10000000000000000)},
  166. accounts[2].addr: {Balance: big.NewInt(10000000000000000)},
  167. }, 10000000,
  168. )
  169. defer contractBackend.Close()
  170. transactOpts, _ := bind.NewKeyedTransactorWithChainID(accounts[0].key, big.NewInt(1337))
  171. // 3 trusted signers, threshold 2
  172. contractAddr, _, c, err := contract.DeployCheckpointOracle(transactOpts, contractBackend, []common.Address{accounts[0].addr, accounts[1].addr, accounts[2].addr}, sectionSize, processConfirms, big.NewInt(2))
  173. if err != nil {
  174. t.Error("Failed to deploy registrar contract", err)
  175. }
  176. contractBackend.Commit()
  177. // getRecent returns block height and hash of the head parent.
  178. getRecent := func() (*big.Int, common.Hash) {
  179. parentNumber := new(big.Int).Sub(contractBackend.Blockchain().CurrentHeader().Number, big.NewInt(1))
  180. parentHash := contractBackend.Blockchain().CurrentHeader().ParentHash
  181. return parentNumber, parentHash
  182. }
  183. // collectSig generates specified number signatures.
  184. collectSig := func(index uint64, hash common.Hash, n int, unauthorized *ecdsa.PrivateKey) (v []uint8, r [][32]byte, s [][32]byte) {
  185. for i := 0; i < n; i++ {
  186. sig := signCheckpoint(contractAddr, accounts[i].key, index, hash)
  187. if unauthorized != nil {
  188. sig = signCheckpoint(contractAddr, unauthorized, index, hash)
  189. }
  190. r = append(r, common.BytesToHash(sig[:32]))
  191. s = append(s, common.BytesToHash(sig[32:64]))
  192. v = append(v, sig[64])
  193. }
  194. return v, r, s
  195. }
  196. // insertEmptyBlocks inserts a batch of empty blocks to blockchain.
  197. insertEmptyBlocks := func(number int) {
  198. for i := 0; i < number; i++ {
  199. contractBackend.Commit()
  200. }
  201. }
  202. // assert checks whether the current contract status is same with
  203. // the expected.
  204. assert := func(index uint64, hash [32]byte, height *big.Int) error {
  205. lindex, lhash, lheight, err := c.GetLatestCheckpoint(nil)
  206. if err != nil {
  207. return err
  208. }
  209. if lindex != index {
  210. return errors.New("latest checkpoint index mismatch")
  211. }
  212. if !bytes.Equal(lhash[:], hash[:]) {
  213. return errors.New("latest checkpoint hash mismatch")
  214. }
  215. if lheight.Cmp(height) != 0 {
  216. return errors.New("latest checkpoint height mismatch")
  217. }
  218. return nil
  219. }
  220. // Test future checkpoint registration
  221. validateOperation(t, c, contractBackend, func() {
  222. number, hash := getRecent()
  223. v, r, s := collectSig(0, checkpoint0.Hash(), 2, nil)
  224. c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
  225. }, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
  226. return assert(0, emptyHash, big.NewInt(0))
  227. }, "test future checkpoint registration")
  228. insertEmptyBlocks(int(sectionSize.Uint64() + processConfirms.Uint64()))
  229. // Test transaction replay protection
  230. validateOperation(t, c, contractBackend, func() {
  231. number, _ := getRecent()
  232. v, r, s := collectSig(0, checkpoint0.Hash(), 2, nil)
  233. hash := common.HexToHash("deadbeef")
  234. c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
  235. }, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
  236. return assert(0, emptyHash, big.NewInt(0))
  237. }, "test transaction replay protection")
  238. // Test unauthorized signature checking
  239. validateOperation(t, c, contractBackend, func() {
  240. number, hash := getRecent()
  241. u, _ := crypto.GenerateKey()
  242. v, r, s := collectSig(0, checkpoint0.Hash(), 2, u)
  243. c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
  244. }, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
  245. return assert(0, emptyHash, big.NewInt(0))
  246. }, "test unauthorized signature checking")
  247. // Test un-multi-signature checkpoint registration
  248. validateOperation(t, c, contractBackend, func() {
  249. number, hash := getRecent()
  250. v, r, s := collectSig(0, checkpoint0.Hash(), 1, nil)
  251. c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
  252. }, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
  253. return assert(0, emptyHash, big.NewInt(0))
  254. }, "test un-multi-signature checkpoint registration")
  255. // Test valid checkpoint registration
  256. validateOperation(t, c, contractBackend, func() {
  257. number, hash := getRecent()
  258. v, r, s := collectSig(0, checkpoint0.Hash(), 2, nil)
  259. c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
  260. }, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
  261. if valid, recv := validateEvents(2, events); !valid {
  262. return errors.New("receive incorrect number of events")
  263. } else {
  264. for i := 0; i < len(recv); i++ {
  265. event := recv[i].Interface().(*contract.CheckpointOracleNewCheckpointVote)
  266. if !assertSignature(contractAddr, event.Index, event.CheckpointHash, event.R, event.S, event.V, accounts[i].addr) {
  267. return errors.New("recover signer failed")
  268. }
  269. }
  270. }
  271. number, _ := getRecent()
  272. return assert(0, checkpoint0.Hash(), number.Add(number, big.NewInt(1)))
  273. }, "test valid checkpoint registration")
  274. distance := 3*sectionSize.Uint64() + processConfirms.Uint64() - contractBackend.Blockchain().CurrentHeader().Number.Uint64()
  275. insertEmptyBlocks(int(distance))
  276. // Test uncontinuous checkpoint registration
  277. validateOperation(t, c, contractBackend, func() {
  278. number, hash := getRecent()
  279. v, r, s := collectSig(2, checkpoint2.Hash(), 2, nil)
  280. c.SetCheckpoint(transactOpts, number, hash, checkpoint2.Hash(), 2, v, r, s)
  281. }, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
  282. if valid, recv := validateEvents(2, events); !valid {
  283. return errors.New("receive incorrect number of events")
  284. } else {
  285. for i := 0; i < len(recv); i++ {
  286. event := recv[i].Interface().(*contract.CheckpointOracleNewCheckpointVote)
  287. if !assertSignature(contractAddr, event.Index, event.CheckpointHash, event.R, event.S, event.V, accounts[i].addr) {
  288. return errors.New("recover signer failed")
  289. }
  290. }
  291. }
  292. number, _ := getRecent()
  293. return assert(2, checkpoint2.Hash(), number.Add(number, big.NewInt(1)))
  294. }, "test uncontinuous checkpoint registration")
  295. // Test old checkpoint registration
  296. validateOperation(t, c, contractBackend, func() {
  297. number, hash := getRecent()
  298. v, r, s := collectSig(1, checkpoint1.Hash(), 2, nil)
  299. c.SetCheckpoint(transactOpts, number, hash, checkpoint1.Hash(), 1, v, r, s)
  300. }, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
  301. number, _ := getRecent()
  302. return assert(2, checkpoint2.Hash(), number)
  303. }, "test uncontinuous checkpoint registration")
  304. // Test stale checkpoint registration
  305. validateOperation(t, c, contractBackend, func() {
  306. number, hash := getRecent()
  307. v, r, s := collectSig(2, checkpoint2.Hash(), 2, nil)
  308. c.SetCheckpoint(transactOpts, number, hash, checkpoint2.Hash(), 2, v, r, s)
  309. }, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
  310. number, _ := getRecent()
  311. return assert(2, checkpoint2.Hash(), number.Sub(number, big.NewInt(1)))
  312. }, "test stale checkpoint registration")
  313. }