update tic-tac-toe milestone project to v0.23.0 (#47)
This commit is contained in:
parent
ab6ef915aa
commit
4744a13768
|
@ -24,9 +24,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-access-control"
|
||||
version = "0.22.1"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb45cc9d1ce72e5eda341126de495a2c3810108c2333c6f3b4e09d99605f3f48"
|
||||
checksum = "fb917e636aa85cbb0d908e948cf7646c78a3e2fb06f396522d01fa55ec93412f"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -38,9 +38,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-account"
|
||||
version = "0.22.1"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16406bd1c27ff4ebdca4f5d5b09b7952f4d161f25094243e09355797c6bddaa6"
|
||||
checksum = "40132c6a9ecf26f6a1d0480824d4e7327bade791d6afe2e003546f58b450e760"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -53,9 +53,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-constant"
|
||||
version = "0.22.1"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d347ce462ceba4473d216bab2c9d0d9702a027d25e93b5376d8d8593d9e13de0"
|
||||
checksum = "90b229e8eb84adf4c282b45bf03354a265f629cb657323728570d994ca0f8ef0"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"proc-macro2",
|
||||
|
@ -64,9 +64,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-error"
|
||||
version = "0.22.1"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "354582d796f8309252d18f787f0e49df8ab6fdfe48f838f059f001ee2f04b5c8"
|
||||
checksum = "0cc0aaf2dae975810ee9f09135cb91b4df66d3834c9bbb789f0930fde2b2a49c"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"proc-macro2",
|
||||
|
@ -76,9 +76,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-event"
|
||||
version = "0.22.1"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a2e218dd8a446993463e38c00159349ae25aa76076191cde0ba460c9c65a180"
|
||||
checksum = "1aefe4f159ae2ccaa908ad7625639294be02602543c5ea8e7b6c8c6126640a8e"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -89,9 +89,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-interface"
|
||||
version = "0.22.1"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e1e536e15b13e3168cf878a90b1bd2dfff1b4c8c9475be4b87f71b20cf8e85d"
|
||||
checksum = "7885efaa71230455a55f2078c130c8168ba58a01271d769a9e3e7753041b023b"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -103,9 +103,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-program"
|
||||
version = "0.22.1"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6519b3ac626c1bd9df407fe22ec6a283f4b1067ee7f3be896ca580be510b7196"
|
||||
checksum = "f962fa82552a2d36ac0a72410a7a63dce16ff84d5921f3195cf1427c89d3848d"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -116,9 +116,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-state"
|
||||
version = "0.22.1"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88e6a21070bcb053f092a1a9054924e8a1b5afd68f7317d0138327401ac154e1"
|
||||
checksum = "acbb91124f1e49d5d84b4c812f47442830ddf8e5b25e7e7fbbe8027ea9a55f4e"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -129,9 +129,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-derive-accounts"
|
||||
version = "0.22.1"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09a65890c2132f30a3ff160fb83f74e0a0454f904f46f1c9be38d3e94c2d06ed"
|
||||
checksum = "8acbc8ab9e4305ae5fdfecc0e40f1254db28747d0bb4a4adf0d63dd09366bac6"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -142,9 +142,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-lang"
|
||||
version = "0.22.1"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef066f4bc0cb4080ff6244b6a66ef31b6077e0302738b365ca894540f5b7dcf8"
|
||||
checksum = "720b81290f0323ab655380fe80d6aeb678c77500e07e5d75e7693b611895b5a4"
|
||||
dependencies = [
|
||||
"anchor-attribute-access-control",
|
||||
"anchor-attribute-account",
|
||||
|
@ -166,9 +166,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-syn"
|
||||
version = "0.22.1"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "506cb44e4e895f917566c7a0554e487a001041d82dd3ae9f1f37ae7f20f86222"
|
||||
checksum = "b18fdb2a5bba16db5d2121935284d3fe226cf35ef1c549162abcb2c50c5b7e21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58 0.3.1",
|
||||
|
@ -178,7 +178,7 @@ dependencies = [
|
|||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.9.9",
|
||||
"sha2",
|
||||
"syn",
|
||||
"thiserror",
|
||||
]
|
||||
|
@ -197,9 +197,20 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
|||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.2"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
|
@ -228,24 +239,19 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "blake3"
|
||||
version = "1.3.1"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f"
|
||||
checksum = "b64485778c4f16a6a5a9d335e80d449ac6c70cdd6a06d2af18a6f6f775a125b3"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"constant_time_eq",
|
||||
"digest 0.10.3",
|
||||
"crypto-mac",
|
||||
"digest 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -254,17 +260,15 @@ version = "0.9.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"block-padding",
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.2"
|
||||
name = "block-padding"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
||||
|
||||
[[package]]
|
||||
name = "borsh"
|
||||
|
@ -323,12 +327,6 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
|
||||
|
||||
[[package]]
|
||||
name = "bv"
|
||||
version = "0.11.1"
|
||||
|
@ -371,32 +369,18 @@ version = "1.0.73"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494"
|
||||
dependencies = [
|
||||
"log",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
|
@ -405,9 +389,9 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
|||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
|
||||
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -418,57 +402,45 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"generic-array 0.14.5",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "3.2.1"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0"
|
||||
checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest 0.9.0",
|
||||
"digest 0.8.1",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
dependencies = [
|
||||
"generic-array 0.12.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.2",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -477,12 +449,34 @@ version = "1.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "feature-probe"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.5"
|
||||
|
@ -500,11 +494,9 @@ version = "0.1.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -513,7 +505,7 @@ version = "0.2.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||
]
|
||||
|
@ -536,6 +528,21 @@ dependencies = [
|
|||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.8.1"
|
||||
|
@ -553,15 +560,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1"
|
||||
dependencies = [
|
||||
"digest 0.9.0",
|
||||
"generic-array",
|
||||
"generic-array 0.14.5",
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.3"
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
@ -572,15 +585,6 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.0"
|
||||
|
@ -595,15 +599,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.119"
|
||||
version = "0.2.121"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
|
||||
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
|
||||
|
||||
[[package]]
|
||||
name = "libsecp256k1"
|
||||
version = "0.6.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73"
|
||||
checksum = "bd1137239ab33b41aa9637a88a28249e5e70c40a42ccc92db7f12cc356c1fcd7"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"base64 0.12.3",
|
||||
|
@ -614,7 +618,7 @@ dependencies = [
|
|||
"libsecp256k1-gen-genmult",
|
||||
"rand",
|
||||
"serde",
|
||||
"sha2 0.9.9",
|
||||
"sha2",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
|
@ -647,22 +651,13 @@ dependencies = [
|
|||
"libsecp256k1-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -673,9 +668,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
|||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.5.3"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f"
|
||||
checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -712,29 +707,6 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
|
@ -774,9 +746,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.15"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
|
||||
checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -822,15 +794,6 @@ dependencies = [
|
|||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.5"
|
||||
|
@ -850,9 +813,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
|||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
@ -870,16 +833,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.6"
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
|
@ -927,64 +893,50 @@ version = "0.9.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if",
|
||||
"block-buffer",
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.10.1"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86"
|
||||
checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809"
|
||||
dependencies = [
|
||||
"digest 0.10.3",
|
||||
"block-buffer",
|
||||
"digest 0.9.0",
|
||||
"keccak",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||
|
||||
[[package]]
|
||||
name = "solana-frozen-abi"
|
||||
version = "1.10.0"
|
||||
version = "1.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f704637b29f1d58b819601efede8eff0998ec10381cb96796dacfe4cfea5581"
|
||||
checksum = "425be155319bda665dc3483f0c0267ac0fc89017812d0c5d9816b1c7a26c1532"
|
||||
dependencies = [
|
||||
"bs58 0.4.0",
|
||||
"bs58 0.3.1",
|
||||
"bv",
|
||||
"generic-array",
|
||||
"generic-array 0.14.5",
|
||||
"log",
|
||||
"memmap2",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"sha2 0.10.2",
|
||||
"sha2",
|
||||
"solana-frozen-abi-macro",
|
||||
"solana-logger",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-frozen-abi-macro"
|
||||
version = "1.10.0"
|
||||
version = "1.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96bf045e938c042c59739ba3a77bf1d25cb7cf073bbf3690cc2d56c7cff27ba2"
|
||||
checksum = "8d97737c34380c42c9b3e060cf68d1929ad81fea5a3c00887bb82314b788ba13"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -993,54 +945,60 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-program"
|
||||
version = "1.10.0"
|
||||
name = "solana-logger"
|
||||
version = "1.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "888f50c71dc45a528cb63df960e657601fe6fa3d643159d93ebff1dd1cc00b63"
|
||||
checksum = "659d836ac49f5a53481ead26f4ea78b688a91dedcbe6c51454169491e1648ceb"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-program"
|
||||
version = "1.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b2b6d99b5c662975ead69a60ead75b820f2eaa42eb4512c79a919e91807d43"
|
||||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
"bincode",
|
||||
"bitflags",
|
||||
"blake3",
|
||||
"borsh",
|
||||
"borsh-derive",
|
||||
"bs58 0.4.0",
|
||||
"bs58 0.3.1",
|
||||
"bv",
|
||||
"bytemuck",
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"curve25519-dalek",
|
||||
"getrandom 0.1.16",
|
||||
"hex",
|
||||
"itertools",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"libsecp256k1",
|
||||
"log",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"parking_lot",
|
||||
"rand",
|
||||
"rustc_version",
|
||||
"rustversion",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_derive",
|
||||
"sha2 0.10.2",
|
||||
"sha2",
|
||||
"sha3",
|
||||
"solana-frozen-abi",
|
||||
"solana-frozen-abi-macro",
|
||||
"solana-logger",
|
||||
"solana-sdk-macro",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-sdk-macro"
|
||||
version = "1.10.0"
|
||||
version = "1.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2388e9b1690e83413393f3e20d98551cbebae9d9385397600d25ae9992873736"
|
||||
checksum = "a122a01e936f3b69064f0800e0488617833fc6a4dd86294cf7cc75f34511d6b5"
|
||||
dependencies = [
|
||||
"bs58 0.4.0",
|
||||
"bs58 0.3.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
|
@ -1055,15 +1013,24 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.86"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
|
||||
checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
|
@ -1139,111 +1106,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.79"
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.79"
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.79"
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
|
@ -1253,6 +1144,6 @@ checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
|
|||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.3.0"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
|
||||
checksum = "7eb5728b8afd3f280a869ce1d4c554ffaed35f45c231fc41bfbd0381bef50317"
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"@project-serum/anchor": "^0.22.1"
|
||||
"@project-serum/anchor": "^0.23.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/chai": "^4.3.0",
|
||||
"chai": "^4.3.4",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"mocha": "^9.0.3",
|
||||
|
|
|
@ -16,6 +16,6 @@ cpi = ["no-entrypoint"]
|
|||
default = []
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = "0.22.1"
|
||||
anchor-lang = "=0.23.0"
|
||||
num-traits = "0.2"
|
||||
num-derive = "0.3"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use anchor_lang::prelude::*;
|
||||
use anchor_lang::error_code;
|
||||
|
||||
#[error_code]
|
||||
pub enum TicTacToeError {
|
||||
|
|
|
@ -5,8 +5,9 @@ use anchor_lang::prelude::*;
|
|||
pub fn play(ctx: Context<Play>, tile: Tile) -> Result<()> {
|
||||
let game = &mut ctx.accounts.game;
|
||||
|
||||
require!(
|
||||
game.current_player() == ctx.accounts.player.key(),
|
||||
require_keys_eq!(
|
||||
game.current_player(),
|
||||
ctx.accounts.player.key(),
|
||||
TicTacToeError::NotPlayersTurn
|
||||
);
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ use crate::state::game::*;
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
pub fn setup_game(ctx: Context<SetupGame>, player_two: Pubkey) -> Result<()> {
|
||||
let game = &mut ctx.accounts.game;
|
||||
game.set_players([ctx.accounts.player_one.key(), player_two]);
|
||||
game.start()
|
||||
ctx.accounts
|
||||
.game
|
||||
.start([ctx.accounts.player_one.key(), player_two])
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
|
|
|
@ -4,35 +4,22 @@ use num_derive::*;
|
|||
use num_traits::*;
|
||||
use std::mem;
|
||||
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct Tile {
|
||||
row: u8,
|
||||
column: u8,
|
||||
}
|
||||
|
||||
#[account]
|
||||
#[derive(Default)]
|
||||
pub struct Game {
|
||||
players: [Pubkey; 2], // 64
|
||||
players: [Pubkey; 2], // (32 * 2)
|
||||
turn: u8, // 1
|
||||
board: [[Option<Sign>; 3]; 3], // 9 * (1 + 1) = 18
|
||||
state: GameState, // 32 + 1
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub const MAXIMUM_SIZE: usize = mem::size_of::<Game>() + 9;
|
||||
pub const MAXIMUM_SIZE: usize = (32 * 2) + 1 + (9 * (1 + 1)) + (32 + 1);
|
||||
|
||||
pub fn start(&mut self) -> Result<()> {
|
||||
if self.turn == 0 {
|
||||
self.turn = 1;
|
||||
Ok(())
|
||||
} else {
|
||||
err!(TicTacToeError::GameAlreadyStarted)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_players(&mut self, players: [Pubkey; 2]) {
|
||||
pub fn start(&mut self, players: [Pubkey; 2]) -> Result<()> {
|
||||
require_eq!(self.turn, 0, TicTacToeError::GameAlreadyStarted);
|
||||
self.players = players;
|
||||
self.turn = 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_active(&self) -> bool {
|
||||
|
@ -132,12 +119,6 @@ pub enum GameState {
|
|||
Won { winner: Pubkey },
|
||||
}
|
||||
|
||||
impl Default for GameState {
|
||||
fn default() -> Self {
|
||||
Self::Active
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
AnchorSerialize, AnchorDeserialize, FromPrimitive, ToPrimitive, Copy, Clone, PartialEq, Eq,
|
||||
)]
|
||||
|
@ -145,3 +126,9 @@ pub enum Sign {
|
|||
X,
|
||||
O,
|
||||
}
|
||||
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct Tile {
|
||||
row: u8,
|
||||
column: u8,
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as anchor from '@project-serum/anchor';
|
||||
import { Program } from '@project-serum/anchor';
|
||||
import { AnchorError, Program } from '@project-serum/anchor';
|
||||
import { TicTacToe } from '../target/types/tic_tac_toe';
|
||||
import chai from 'chai';
|
||||
import chaiAsPromised from 'chai-as-promised';
|
||||
|
@ -109,8 +109,13 @@ describe('tic-tac-toe', () => {
|
|||
]
|
||||
);
|
||||
chai.assert(false, "should've failed but didn't ");
|
||||
} catch (err) {
|
||||
expect(err.code).to.equal(6003);
|
||||
} catch (_err) {
|
||||
expect(_err).to.be.instanceOf(AnchorError);
|
||||
const err: AnchorError = _err;
|
||||
expect(err.error.errorCode.code).to.equal("NotPlayersTurn");
|
||||
expect(err.error.errorCode.number).to.equal(6003);
|
||||
expect(err.program.equals(program.programId)).is.true;
|
||||
expect(err.error.comparedValues).to.deep.equal([playerTwo.publicKey, playerOne.publicKey]);
|
||||
}
|
||||
|
||||
await play(
|
||||
|
@ -156,8 +161,11 @@ describe('tic-tac-toe', () => {
|
|||
]
|
||||
);
|
||||
chai.assert(false, "should've failed but didn't ");
|
||||
} catch (error) {
|
||||
expect(error.code).to.equal(6000);
|
||||
} catch (_err) {
|
||||
expect(_err).to.be.instanceOf(AnchorError);
|
||||
const err: AnchorError = _err;
|
||||
expect(err.error.errorCode.number).to.equal(6000);
|
||||
expect(err.error.errorCode.code).to.equal("TileOutOfBounds");
|
||||
}
|
||||
|
||||
await play(
|
||||
|
@ -189,8 +197,10 @@ describe('tic-tac-toe', () => {
|
|||
]
|
||||
);
|
||||
chai.assert(false, "should've failed but didn't ");
|
||||
} catch (error) {
|
||||
expect(error.code).to.equal(6001);
|
||||
} catch (_err) {
|
||||
expect(_err).to.be.instanceOf(AnchorError);
|
||||
const err: AnchorError = _err;
|
||||
expect(err.error.errorCode.number).to.equal(6001);
|
||||
}
|
||||
|
||||
await play(
|
||||
|
@ -222,8 +232,10 @@ describe('tic-tac-toe', () => {
|
|||
]
|
||||
);
|
||||
chai.assert(false, "should've failed but didn't ");
|
||||
} catch (error) {
|
||||
expect(error.code).to.equal(6002);
|
||||
} catch (_err) {
|
||||
expect(_err).to.be.instanceOf(AnchorError);
|
||||
const err: AnchorError = _err;
|
||||
expect(err.error.errorCode.number).to.equal(6002);
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -30,18 +30,19 @@
|
|||
"@ethersproject/logger" "^5.5.0"
|
||||
hash.js "1.1.7"
|
||||
|
||||
"@project-serum/anchor@^0.20.1":
|
||||
version "0.20.1"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.20.1.tgz#0937807e807e8332aa708cfef4bcb6cbb88b4129"
|
||||
integrity sha512-2TuBmGUn9qeYz6sJINJlElrBuPsaUAtYyUsJ3XplEBf1pczrANAgs5ceJUFzdiqGEWLn+84ObSdBeChT/AXYFA==
|
||||
"@project-serum/anchor@^0.23.0":
|
||||
version "0.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.23.0.tgz#2b2eb6b51601b073e8db26663aa2d6c2f2841771"
|
||||
integrity sha512-LV2/ifZOJVFTZ4GbEloXln3iVfCvO1YM8i7BBCrUm4tehP7irMx4nr4/IabHWOzrQcQElsxSP/lb1tBp+2ff8A==
|
||||
dependencies:
|
||||
"@project-serum/borsh" "^0.2.2"
|
||||
"@solana/web3.js" "^1.17.0"
|
||||
"@project-serum/borsh" "^0.2.5"
|
||||
"@solana/web3.js" "^1.36.0"
|
||||
base64-js "^1.5.1"
|
||||
bn.js "^5.1.2"
|
||||
bs58 "^4.0.1"
|
||||
buffer-layout "^1.2.2"
|
||||
camelcase "^5.3.1"
|
||||
cross-fetch "^3.1.5"
|
||||
crypto-hash "^1.3.0"
|
||||
eventemitter3 "^4.0.7"
|
||||
find "^0.3.0"
|
||||
|
@ -50,10 +51,10 @@
|
|||
snake-case "^3.0.4"
|
||||
toml "^3.0.0"
|
||||
|
||||
"@project-serum/borsh@^0.2.2":
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.3.tgz#1d705c5887484cb6a127dd5feff58e90cbfcb558"
|
||||
integrity sha512-lH9zEYADZE3cxrgiFym8+jbUE3NM/LH+WOKYcUjs65CT10Q64Hv45bcAAa/phwYk4Tpz0uQ1x+ergFaAoGt67Q==
|
||||
"@project-serum/borsh@^0.2.5":
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663"
|
||||
integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q==
|
||||
dependencies:
|
||||
bn.js "^5.1.2"
|
||||
buffer-layout "^1.2.0"
|
||||
|
@ -65,10 +66,10 @@
|
|||
dependencies:
|
||||
buffer "~6.0.3"
|
||||
|
||||
"@solana/web3.js@^1.17.0":
|
||||
version "1.31.0"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.31.0.tgz#7a313d4c1a90b77f27ddbfe845a10d6883e06452"
|
||||
integrity sha512-7nHHx1JNFnrt15e9y8m38I/EJCbaB+bFC3KZVM1+QhybCikFxGMtGA5r7PDC3GEL1R2RZA8yKoLkDKo3vzzqnw==
|
||||
"@solana/web3.js@^1.36.0":
|
||||
version "1.36.0"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.36.0.tgz#79d7d5217b49b80139f4de68953adc5b9a9a264f"
|
||||
integrity sha512-RNT1451iRR7TyW7EJKMCrH/0OXawIe4zVm0DWQASwXlR/u1jmW6FrmH0lujIh7cGTlfOVbH+2ZU9AVUPLBFzwA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@ethersproject/sha2" "^5.5.0"
|
||||
|
@ -92,6 +93,11 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/chai@^4.3.0":
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc"
|
||||
integrity sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==
|
||||
|
||||
"@types/connect@^3.4.33":
|
||||
version "3.4.35"
|
||||
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
|
||||
|
@ -410,6 +416,13 @@ cross-fetch@^3.1.4:
|
|||
dependencies:
|
||||
node-fetch "2.6.1"
|
||||
|
||||
cross-fetch@^3.1.5:
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
|
||||
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
|
||||
dependencies:
|
||||
node-fetch "2.6.7"
|
||||
|
||||
crypto-hash@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247"
|
||||
|
@ -851,6 +864,13 @@ node-fetch@2.6.1:
|
|||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
node-fetch@2.6.7:
|
||||
version "2.6.7"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
|
||||
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-gyp-build@^4.2.0, node-gyp-build@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3"
|
||||
|
@ -1054,6 +1074,11 @@ toml@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee"
|
||||
integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
|
||||
|
||||
traverse-chain@~0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1"
|
||||
|
@ -1124,6 +1149,19 @@ uuid@^8.3.0, uuid@^8.3.2:
|
|||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||
integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
|
||||
dependencies:
|
||||
tr46 "~0.0.3"
|
||||
webidl-conversions "^3.0.0"
|
||||
|
||||
which@2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||
|
|
|
@ -9,7 +9,7 @@ anchor init tic-tac-toe
|
|||
|
||||
The program will have 2 instructions. First, we need to setup the game. We need to save who is playing it and create a board to play on. Then, the player take turns until there is a winner or a tie.
|
||||
|
||||
We recommend to keep programs in a single `lib.rs` file until they get too big. We would keep this program in a single file too but have split up the reference implementation for demonstration purposes. Check out the section at the end of this chapter to learn how to split up a program into multiple files.
|
||||
We recommend keeping programs in a single `lib.rs` file until they get too big. We would not split up this project into multiple files either but there is a section at the end of this chapter that explains how to do it for this and other programs.
|
||||
|
||||
## Setting up the game
|
||||
|
||||
|
@ -19,7 +19,7 @@ Let's begin by thinking about which data we should store. Each game has players,
|
|||
```rust,ignore
|
||||
#[account]
|
||||
pub struct Game {
|
||||
players: [Pubkey; 2], // 64
|
||||
players: [Pubkey; 2], // (32 * 2)
|
||||
turn: u8, // 1
|
||||
board: [[Option<Sign>; 3]; 3], // 9 * (1 + 1) = 18
|
||||
state: GameState, // 32 + 1
|
||||
|
@ -63,144 +63,19 @@ use num_derive::*;
|
|||
use num_traits::*;
|
||||
```
|
||||
|
||||
### The Setup Instruction
|
||||
|
||||
Before we write any game logic, we can add the instruction that will set up the game in its initial state. Rename the already existing instruction function and accounts struct to `setup_game` and `SetupGame` respectively. Now think about which accounts are needed to set up the game. Clearly, we need the game account. Before we can fill it with values, we need to create it. For that, we use the `init` constraint.
|
||||
```rust,ignore
|
||||
#[derive(Accounts)]
|
||||
pub struct SetupGame<'info> {
|
||||
#[account(init)]
|
||||
pub game: Account<'info, Game>
|
||||
}
|
||||
```
|
||||
`init` immediately shouts at us and tells us to add a payer. Why do we need it? Because `init` creates `rent-exempt` accounts and someone has to pay for that. Naturally, if we want to take money from someone, we should make them sign as well as mark their account as mutable.
|
||||
```rust,ignore
|
||||
#[derive(Accounts)]
|
||||
pub struct SetupGame<'info> {
|
||||
#[account(init, payer = player_one)]
|
||||
pub game: Account<'info, Game>,
|
||||
#[account(mut)]
|
||||
pub player_one: Signer<'info>
|
||||
}
|
||||
```
|
||||
`init` is not happy yet. It wants the system program to be inside the struct because `init` creates the game account by making a call to that program. So let's add it.
|
||||
```rust,ignore
|
||||
#[derive(Accounts)]
|
||||
pub struct SetupGame<'info> {
|
||||
#[account(init, payer = player_one)]
|
||||
pub game: Account<'info, Game>,
|
||||
#[account(mut)]
|
||||
pub player_one: Signer<'info>,
|
||||
pub system_program: Program<'info, System>
|
||||
}
|
||||
```
|
||||
There's one more thing to do to complete `SetupGame`. Every account is created with a fixed amount of space. `init` can try to infer how much space an account needs if it derives `Default`. So let's implement `Default` for `Game`
|
||||
```rust,ignore
|
||||
#[account]
|
||||
#[derive(Default)] <-- add this
|
||||
pub struct Game {...
|
||||
```
|
||||
and `GameState`.
|
||||
```rust,ignore
|
||||
impl Default for GameState {
|
||||
fn default() -> Self {
|
||||
Self::Active
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And with this, `SetupGame` is complete and we can move on to the `setup_game` function. (If you like playing detective, you can pause here and try to figure out why what we just did will not work. Hint: Have a look at the [specification](https://borsh.io/) of the serialization library Anchor uses. If you cannot figure it out, don't worry. We are going to fix it very soon, together.)
|
||||
|
||||
Let's start by adding an argument to the `setup_game` function.
|
||||
```rust,ignore
|
||||
pub fn setup_game(ctx: Context<SetupGame>, player_two: Pubkey) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
Why didn't we just add `player_two` as an account in the accounts struct? There are two reasons for this. First, adding it there requires a little more space in the transaction that saves whether the account is writable and whether it's a signer. But we care about neither of that. We just want the address. This brings us to the second and more important reason: Simultaneous network transactions can affect each other if they share the same accounts. For example, if we add `player_two` to the accounts struct, during our transaction, no other transaction can edit `player_two`'s account. Therefore, we block all other transactions that want to edit `player_two`'s account, even though we neither want to read from nor write to the account. We just care about its address!
|
||||
|
||||
Finish the instruction function by setting the game to its initial values:
|
||||
```rust,ignore
|
||||
pub fn setup_game(ctx: Context<SetupGame>, player_two: Pubkey) -> Result<()> {
|
||||
let game = &mut ctx.accounts.game;
|
||||
game.players = [ctx.accounts.player_one.key(), player_two];
|
||||
game.turn = 1;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
Now, run `anchor build`. On top of compiling your program, this command creates an [IDL](https://en.wikipedia.org/wiki/Interface_description_language) for your program. You can find it in `target/idl`. The anchor typescript client can automatically parse this IDL and generate functions based on it. What this means is that each anchor program gets its own typescript client for free! (Technically, you don't have to call anchor build before testing. `anchor test` will do it for you.)
|
||||
|
||||
### Testing the Setup Instruction
|
||||
|
||||
Time to test our code! Head over into the `tests` folder in the root directory. Open the `tic-tac-toe.ts` file and remove the existing `it` test. Then, put the following into the `describe` section:
|
||||
```typescript
|
||||
it('setup game!', async() => {
|
||||
const gameKeypair = anchor.web3.Keypair.generate();
|
||||
const playerOne = program.provider.wallet;
|
||||
const playerTwo = anchor.web3.Keypair.generate();
|
||||
await program.rpc.setupGame(playerTwo.publicKey, {
|
||||
accounts: {
|
||||
game: gameKeypair.publicKey,
|
||||
playerOne: playerOne.publicKey,
|
||||
systemProgram: anchor.web3.SystemProgram.programId
|
||||
},
|
||||
signers: [gameKeypair]
|
||||
});
|
||||
|
||||
let gameState = await program.account.game.fetch(gameKeypair.publicKey);
|
||||
expect(gameState.turn).to.equal(1);
|
||||
expect(gameState.players)
|
||||
.to
|
||||
.eql([playerOne.publicKey, playerTwo.publicKey]);
|
||||
expect(gameState.state).to.eql({ active: {} });
|
||||
expect(gameState.board)
|
||||
.to
|
||||
.eql([[null,null,null],[null,null,null],[null,null,null]]);
|
||||
});
|
||||
```
|
||||
|
||||
and add this to the top of your file:
|
||||
```typescript
|
||||
import { expect } from 'chai';
|
||||
```
|
||||
|
||||
The test begins by creating some keypairs. Importantly, `playerOne` is not a keypair but the wallet of the program's provider. The provider details are defined in the `Anchor.toml` file in the root of the project.
|
||||
Then, we send the transaction. Because the anchor typescript client has parsed the IDL, all transaction inputs have types. If you remove one of the accounts for example, typescript will complain.
|
||||
The structure of the transaction function is as follows: First come the instruction arguments. For this function, the public key of the second player. Then come the accounts. Lastly, we add a signers array. We have to add the `gameKeypair` here because whenever an account gets created, it has to sign its creation transaction. We don't have to add `playerOne` even though we gave it the `Signer` type in the program because it is the program provider and therefore signs the transaction by default.
|
||||
|
||||
After the transaction returns, we can fetch the state of the game account. You can fetch account state using the `program.account` namespace.
|
||||
Finally, we verify the game has been set up properly. Anchor's typescript client deserializes rust enums like this: `{ active: {}}` for a fieldless variant and `{ won: { winner: Pubkey }}` for a variant with fields. The `None` variant of `Option` becomes `null`. The `Some(x)` variant becomes whatever `x` deserializes to.
|
||||
|
||||
Now, run `anchor test`. This starts up (and subsequently shuts down) a local validator (make sure you don't have one running) and runs your tests using the test script defined in `Anchor.toml`.
|
||||
|
||||
> When you adjust your test files it may happen that you'll see errors everywhere.
|
||||
> This is likely because the test file is looking for types from your program that haven't been generated yet.
|
||||
> To generate them, run `anchor build`. This builds the program and creates the idl and typescript types.
|
||||
|
||||
|
||||
If you get the error `Error: Unable to read keypair file` when running the test, you likely need to generate a Solana keypair using `solana-keygen new`.
|
||||
|
||||
## Playing the game
|
||||
|
||||
### The Play Instruction
|
||||
|
||||
The `Play` accounts struct is straightforward. We need the game and a player:
|
||||
```rust,ignore
|
||||
#[derive(Accounts)]
|
||||
pub struct Play<'info> {
|
||||
#[account(mut)]
|
||||
pub game: Account<'info, Game>,
|
||||
pub player: Signer<'info>,
|
||||
}
|
||||
```
|
||||
|
||||
`player` needs to sign or someone else could play for the player.
|
||||
|
||||
Next, add the game logic:
|
||||
Now add the game logic:
|
||||
|
||||
```rust,ignore
|
||||
impl Game {
|
||||
pub const MAXIMUM_SIZE: usize = (32 * 2) + 1 + (9 * (1 + 1)) + (32 + 1);
|
||||
|
||||
pub fn start(&mut self, players: [Pubkey; 2]) -> Result<()> {
|
||||
require_eq!(self.turn, 0, TicTacToeError::GameAlreadyStarted);
|
||||
self.players = players;
|
||||
self.turn = 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.state == GameState::Active
|
||||
}
|
||||
|
@ -217,18 +92,17 @@ impl Game {
|
|||
require!(self.is_active(), TicTacToeError::GameAlreadyOver);
|
||||
|
||||
match tile {
|
||||
tile
|
||||
@ Tile {
|
||||
tile @ Tile {
|
||||
row: 0..=2,
|
||||
column: 0..=2,
|
||||
} => match self.board[tile.row as usize][tile.column as usize] {
|
||||
Some(_) => return err!(TicTacToeError::TileAlreadySet),
|
||||
Some(_) => return Err(TicTacToeError::TileAlreadySet.into()),
|
||||
None => {
|
||||
self.board[tile.row as usize][tile.column as usize] =
|
||||
Some(Sign::from_usize(self.current_player_index()).unwrap());
|
||||
}
|
||||
},
|
||||
_ => return err!(TicTacToeError::TileOutOfBounds),
|
||||
_ => return Err(TicTacToeError::TileOutOfBounds.into()),
|
||||
}
|
||||
|
||||
self.update_state();
|
||||
|
@ -318,16 +192,157 @@ pub enum TicTacToeError {
|
|||
TileAlreadySet,
|
||||
GameAlreadyOver,
|
||||
NotPlayersTurn,
|
||||
GameAlreadyStarted
|
||||
}
|
||||
```
|
||||
|
||||
### The Setup Instruction
|
||||
|
||||
Before we write any game logic, we can add the instruction that will set up the game in its initial state. Rename the already existing instruction function and accounts struct to `setup_game` and `SetupGame` respectively. Now think about which accounts are needed to set up the game. Clearly, we need the game account. Before we can fill it with values, we need to create it. For that, we use the `init` constraint.
|
||||
```rust,ignore
|
||||
#[derive(Accounts)]
|
||||
pub struct SetupGame<'info> {
|
||||
#[account(init)]
|
||||
pub game: Account<'info, Game>
|
||||
}
|
||||
```
|
||||
`init` immediately shouts at us and tells us to add a payer. Why do we need it? Because `init` creates `rent-exempt` accounts and someone has to pay for that. Naturally, if we want to take money from someone, we should make them sign as well as mark their account as mutable.
|
||||
```rust,ignore
|
||||
#[derive(Accounts)]
|
||||
pub struct SetupGame<'info> {
|
||||
#[account(init, payer = player_one)]
|
||||
pub game: Account<'info, Game>,
|
||||
#[account(mut)]
|
||||
pub player_one: Signer<'info>
|
||||
}
|
||||
```
|
||||
`init` is not happy yet. It wants the system program to be inside the struct because `init` creates the game account by making a call to that program. So let's add it.
|
||||
```rust,ignore
|
||||
#[derive(Accounts)]
|
||||
pub struct SetupGame<'info> {
|
||||
#[account(init, payer = player_one)]
|
||||
pub game: Account<'info, Game>,
|
||||
#[account(mut)]
|
||||
pub player_one: Signer<'info>,
|
||||
pub system_program: Program<'info, System>
|
||||
}
|
||||
```
|
||||
|
||||
There's one more thing to do to complete `SetupGame`. Every account is created with a fixed amount of space, so we have to this space to the instruction as well. This is what the comments next to the `Game` struct indicated.
|
||||
```rust,ignore
|
||||
#[derive(Accounts)]
|
||||
pub struct SetupGame<'info> {
|
||||
#[account(init, payer = player_one, space = 8 + Game::MAXIMUM_SIZE)]
|
||||
pub game: Account<'info, Game>,
|
||||
#[account(mut)]
|
||||
pub player_one: Signer<'info>,
|
||||
pub system_program: Program<'info, System>
|
||||
}
|
||||
```
|
||||
|
||||
Let us briefly explain how we arrived at the `Game::MAXIMUM_SIZE`. Anchor uses the [borsh](https://borsh.io) specification to (de)serialize its state accounts.
|
||||
|
||||
- Pubkey has a length of `32` bytes so `2*32 = 64`
|
||||
- u8 as a vector has a length of `1`
|
||||
- the `board` has a length of (`9 * (1 + 1)`). We know the board has 9 tiles (-> `9`) of type `Option` which borsh serializes with 1 byte (set to `1` for Some and `0` for None) plus the size of whatever's in the `Option`. In this case, it's a simple enum with types that don't hold more types so the maximum size of the enum is also just `1` (for its discriminant). In total that means we get `9 (tiles) * (1 (Option) + 1(Sign discriminant))`.
|
||||
- `state` is also an enum so we need `1` byte for the discriminant. We have to init the account with the maximum size and the maximum size of an enum is the size of its biggest variant. In this case that's the `winner` variant which holds a Pubkey. A Pubkey is `32` bytes long so the size of `state` is `1 (discriminant) + 32 (winner pubkey)` (`MAXIMUM_SIZE` is a [`const`](https://doc.rust-lang.org/std/keyword.const.html) variable so specifying it in terms of a sum of the sizes of `Game`'s members' fields does not incur any runtime cost).
|
||||
|
||||
In addition to the game's size, we have to add another 8 to the space. This is space for the internal discriminator which anchor sets automatically. In short, the discriminator is how anchor can differentiate between different accounts of the same program.
|
||||
|
||||
> (What about using `mem::size_of<Game>()`? This almost works but not quite. The issue is that borsh will always serialize an option as 1 byte for the variant identifier and then additional x bytes for the content if it's Some. Rust uses null-pointer optimization to make Option's variant identifier 0 bytes when it can, so an option is sometimes just as big as its contents. This is the case with `Sign`. This means the `MAXIMUM_SIZE` could also be expressed as `mem::size_of<Game>() + 9`.)
|
||||
|
||||
And with this, `SetupGame` is complete and we can move on to the `setup_game` function. (If you like playing detective, you can pause here and try to figure out why what we just did will not work. Hint: Have a look at the [specification](https://borsh.io/) of the serialization library Anchor uses. If you cannot figure it out, don't worry. We are going to fix it very soon, together.)
|
||||
|
||||
Let's start by adding an argument to the `setup_game` function.
|
||||
```rust,ignore
|
||||
pub fn setup_game(ctx: Context<SetupGame>, player_two: Pubkey) -> Result<()> {
|
||||
|
||||
}
|
||||
```
|
||||
Why didn't we just add `player_two` as an account in the accounts struct? There are two reasons for this. First, adding it there requires a little more space in the transaction that saves whether the account is writable and whether it's a signer. But we care about neither the mutability of the account nor whether it's a signer. We just need its address. This brings us to the second and more important reason: Simultaneous network transactions can affect each other if they share the same accounts. For example, if we add `player_two` to the accounts struct, during our transaction, no other transaction can edit `player_two`'s account. Therefore, we block all other transactions that want to edit `player_two`'s account, even though we neither want to read from nor write to the account. We just care about its address!
|
||||
|
||||
Finish the instruction function by setting the game to its initial values:
|
||||
```rust,ignore
|
||||
pub fn setup_game(ctx: Context<SetupGame>, player_two: Pubkey) -> Result<()> {
|
||||
ctx.accounts.game.start([ctx.accounts.player_one.key(), player_two])
|
||||
}
|
||||
```
|
||||
|
||||
Now, run `anchor build`. On top of compiling your program, this command creates an [IDL](https://en.wikipedia.org/wiki/Interface_description_language) for your program. You can find it in `target/idl`. The anchor typescript client can automatically parse this IDL and generate functions based on it. What this means is that each anchor program gets its own typescript client for free! (Technically, you don't have to call `anchor build` before testing. `anchor test` will do it for you.)
|
||||
|
||||
### Testing the Setup Instruction
|
||||
|
||||
Time to test our code! Head over into the `tests` folder in the root directory. Open the `tic-tac-toe.ts` file and remove the existing `it` test. Then, put the following into the `describe` section:
|
||||
```typescript
|
||||
it('setup game!', async() => {
|
||||
const gameKeypair = anchor.web3.Keypair.generate();
|
||||
const playerOne = program.provider.wallet;
|
||||
const playerTwo = anchor.web3.Keypair.generate();
|
||||
await program.rpc.setupGame(playerTwo.publicKey, {
|
||||
accounts: {
|
||||
game: gameKeypair.publicKey,
|
||||
playerOne: playerOne.publicKey,
|
||||
systemProgram: anchor.web3.SystemProgram.programId
|
||||
},
|
||||
signers: [gameKeypair]
|
||||
});
|
||||
|
||||
let gameState = await program.account.game.fetch(gameKeypair.publicKey);
|
||||
expect(gameState.turn).to.equal(1);
|
||||
expect(gameState.players)
|
||||
.to
|
||||
.eql([playerOne.publicKey, playerTwo.publicKey]);
|
||||
expect(gameState.state).to.eql({ active: {} });
|
||||
expect(gameState.board)
|
||||
.to
|
||||
.eql([[null,null,null],[null,null,null],[null,null,null]]);
|
||||
});
|
||||
```
|
||||
|
||||
and add this to the top of your file:
|
||||
```typescript
|
||||
import { expect } from 'chai';
|
||||
```
|
||||
|
||||
> When you adjust your test files it may happen that you'll see errors everywhere.
|
||||
> This is likely because the test file is looking for types from your program that haven't been generated yet.
|
||||
> To generate them, run `anchor build`. This builds the program and creates the idl and typescript types.
|
||||
|
||||
The test begins by creating some keypairs. Importantly, `playerOne` is not a keypair but the wallet of the program's provider. The provider details are defined in the `Anchor.toml` file in the root of the project.
|
||||
Then, we send the transaction. Because the anchor typescript client has parsed the IDL, all transaction inputs have types. If you remove one of the accounts for example, typescript will complain.
|
||||
The structure of the transaction function is as follows: First come the instruction arguments. For this function, the public key of the second player. Then come the accounts. Lastly, we add a signers array. We have to add the `gameKeypair` here because whenever an account gets created, it has to sign its creation transaction. We don't have to add `playerOne` even though we gave it the `Signer` type in the program because it is the program provider and therefore signs the transaction by default.
|
||||
|
||||
After the transaction returns, we can fetch the state of the game account. You can fetch account state using the `program.account` namespace.
|
||||
Finally, we verify the game has been set up properly. Anchor's typescript client deserializes rust enums like this: `{ active: {}}` for a fieldless variant and `{ won: { winner: Pubkey }}` for a variant with fields. The `None` variant of `Option` becomes `null`. The `Some(x)` variant becomes whatever `x` deserializes to.
|
||||
|
||||
Now, run `anchor test`. This starts up (and subsequently shuts down) a local validator (make sure you don't have one running) and runs your tests using the test script defined in `Anchor.toml`.
|
||||
|
||||
> If you get the error `Error: Unable to read keypair file` when running the test, you likely need to generate a Solana keypair using `solana-keygen new`.
|
||||
|
||||
## Playing the game
|
||||
|
||||
### The Play Instruction
|
||||
|
||||
The `Play` accounts struct is straightforward. We need the game and a player:
|
||||
```rust,ignore
|
||||
#[derive(Accounts)]
|
||||
pub struct Play<'info> {
|
||||
#[account(mut)]
|
||||
pub game: Account<'info, Game>,
|
||||
pub player: Signer<'info>,
|
||||
}
|
||||
```
|
||||
|
||||
`player` needs to sign or someone else could play for the player.
|
||||
|
||||
Finally, we can add the `play` function inside the program module.
|
||||
```rust,ignore
|
||||
pub fn play(ctx: Context<Play>, tile: Tile) -> Result<()> {
|
||||
let game = &mut ctx.accounts.game;
|
||||
|
||||
require!(
|
||||
game.current_player() == ctx.accounts.player.key(),
|
||||
require_keys_eq!(
|
||||
game.current_player(),
|
||||
ctx.accounts.player.key(),
|
||||
TicTacToeError::NotPlayersTurn
|
||||
);
|
||||
|
||||
|
@ -335,7 +350,7 @@ pub fn play(ctx: Context<Play>, tile: Tile) -> Result<()> {
|
|||
}
|
||||
```
|
||||
|
||||
We've checked in the accounts struct that the `player` account has signed the transaction, but we do not check that it is the `player` we expect. That's what the `require` check in `play` is for.
|
||||
We've checked in the accounts struct that the `player` account has signed the transaction, but we do not check that it is the `player` we expect. That's what the `require_keys_eq` check in `play` is for.
|
||||
|
||||
### Testing the Play Instruction
|
||||
|
||||
|
@ -401,69 +416,18 @@ it('player one wins', async() => {
|
|||
});
|
||||
```
|
||||
|
||||
Now run `anchor test` again and you will be greeted with an error:
|
||||
```
|
||||
Error: 3004: Failed to serialize the account
|
||||
```
|
||||
What to do? We know that it happens during play, because our `setupGame` test runs fine.
|
||||
Also, it says `serialize`, not `deserialize`. So after our logic runs and anchor tries to save all the data, there is an error.
|
||||
What this means most of the time is that the account is too small to hold all its data and this is also the problem here.
|
||||
and run `anchor test`.
|
||||
|
||||
Let's have a look at the `Game` struct again and the way we created it:
|
||||
```rust,ignore
|
||||
#[account]
|
||||
#[derive(Default)]
|
||||
pub struct Game {
|
||||
players: [Pubkey; 2], // 64
|
||||
turn: u8, // 1
|
||||
board: [[Option<Sign>; 3]; 3], // 9 * (1 + 1) = 18
|
||||
state: GameState, // 32 + 1
|
||||
}
|
||||
You can finish writing the test by yourself (or check out the [the reference implementation](https://github.com/project-serum/anchor-book/tree/master/programs/tic-tac-toe)). Try to simulate a win and a tie!
|
||||
|
||||
...
|
||||
#[account(init, payer = player_one)]
|
||||
pub game: Account<'info, Game>,
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
Remember that we implemented `Default` for `Game` because `init` can try to infer the correct space requirements based on `Default`, "try" being the operative word. What happens if we don't specify an explicit `space` requirement for the account is that anchor will call `default` on the account and convert it to a vector using borsh. It then uses the length of that vector as the space for the account.
|
||||
Let's walk through our example step by step using the [borsh specification](https://borsh.io/). The comments show us the space requirements that we must get, that is, the largest the given type can become.
|
||||
- Pubkey as a vector has a length of `32` so `2*32 = 64` ✅
|
||||
- u8 as a vector has a length of `1` so `1 = 1` ✅
|
||||
- board's default (`9 * None`) as a vector has a length of `9 != 18` ❌
|
||||
- state's default as a vector is `1 != 33` ❌
|
||||
|
||||
We have found out that `init` currently only allocates 75 bytes for our account data but the account can grow to (64 + 1 + 18 + 33) = 116 bytes.
|
||||
We can add this number to our Game impl like this:
|
||||
```rust,ignore
|
||||
impl Game {
|
||||
const MAXIMUM_SIZE: usize = 116;
|
||||
|
||||
... // other functions
|
||||
}
|
||||
```
|
||||
|
||||
```rust,ignore
|
||||
...
|
||||
#[account(init, payer = player_one, space = Game::MAXIMUM_SIZE + 8)]
|
||||
pub game: Account<'info, Game>,
|
||||
...
|
||||
```
|
||||
In addition to the game's size, we have to add another `8` to the space. This is space for the `discriminator` which anchor sets automatically. In short, the discriminator is how anchor can differentiate between different accounts of the same program.
|
||||
|
||||
> (What about using `mem::size_of<Game>()`? This almost works but not quite. The issue is that borsh will always serialize an option as 1 byte for the variant identifier and then additional x bytes for the content if it's Some. Rust uses null-pointer optimization to make Option's variant identifier 0 bytes when it can, so an option is sometimes just as big as its contents. This is the case with `Sign`. This means the `MAXIMUM_SIZE` could be expressed as `mem::size_of<Game>() + 9`.)
|
||||
|
||||
Running `anchor test` should work now. You can finish writing the test by yourself. Try to simulate a win and a tie!
|
||||
|
||||
Proper testing also includes tests that try to exploit the contract. You can check whether you've protected yourself properly by calling `play` with unexpected parameters. For example:
|
||||
Proper testing also includes tests that try to exploit the contract. You can check whether you've protected yourself properly by calling `play` with unexpected parameters. You can also familiarize yourself with the returned `AnchorErrors` this way. For example:
|
||||
```typescript
|
||||
try {
|
||||
await play(
|
||||
program,
|
||||
gameKeypair.publicKey,
|
||||
playerTwo,
|
||||
{row: 5, column: 1}, // out of bounds row
|
||||
{row: 5, column: 1}, // ERROR: out of bounds row
|
||||
4,
|
||||
{ active: {}, },
|
||||
[
|
||||
|
@ -474,8 +438,42 @@ try {
|
|||
);
|
||||
// we use this to make sure we definitely throw an error
|
||||
chai.assert(false, "should've failed but didn't ");
|
||||
} catch (error) {
|
||||
expect(error.code).to.equal(6000);
|
||||
} catch (_err) {
|
||||
expect(_err).to.be.instanceOf(AnchorError);
|
||||
const err: AnchorError = _err;
|
||||
expect(err.error.errorCode.number).to.equal(6000);
|
||||
}
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await play(
|
||||
program,
|
||||
gameKeypair.publicKey,
|
||||
playerOne, // ERROR: same player in subsequent turns
|
||||
|
||||
// change sth about the tx because
|
||||
// duplicate tx that come in too fast
|
||||
// after each other may get dropped
|
||||
{row: 1, column: 0},
|
||||
2,
|
||||
{ active: {}, },
|
||||
[
|
||||
[{x:{}},null,null],
|
||||
[null,null,null],
|
||||
[null,null,null]
|
||||
]
|
||||
);
|
||||
chai.assert(false, "should've failed but didn't ");
|
||||
} catch (_err) {
|
||||
expect(_err).to.be.instanceOf(AnchorError);
|
||||
const err: AnchorError = _err;
|
||||
expect(err.error.errorCode.code).to.equal("NotPlayersTurn");
|
||||
expect(err.error.errorCode.number).to.equal(6003);
|
||||
expect(err.program.equals(program.programId)).is.true;
|
||||
expect(err.error.comparedValues).to.deep.equal([playerTwo.publicKey, playerOne.publicKey]);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in New Issue