github.com/klaytn/klaytn@v1.12.1/blockchain/types/account/account_test.go (about) 1 // Copyright 2019 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn 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 klaytn 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 klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 17 package account 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "math/big" 23 "math/rand" 24 "testing" 25 26 "github.com/klaytn/klaytn/blockchain/types/accountkey" 27 "github.com/klaytn/klaytn/common" 28 "github.com/klaytn/klaytn/common/hexutil" 29 "github.com/klaytn/klaytn/crypto" 30 "github.com/klaytn/klaytn/crypto/sha3" 31 "github.com/klaytn/klaytn/params" 32 "github.com/klaytn/klaytn/rlp" 33 "github.com/stretchr/testify/assert" 34 ) 35 36 // Compile time interface checks 37 var ( 38 _ Account = (*LegacyAccount)(nil) 39 _ Account = (*ExternallyOwnedAccount)(nil) 40 _ Account = (*SmartContractAccount)(nil) 41 42 _ ProgramAccount = (*SmartContractAccount)(nil) 43 44 _ AccountWithKey = (*ExternallyOwnedAccount)(nil) 45 _ AccountWithKey = (*SmartContractAccount)(nil) 46 ) 47 48 // TestAccountSerialization tests serialization of various account types. 49 func TestAccountSerialization(t *testing.T) { 50 accs := []struct { 51 Name string 52 acc Account 53 }{ 54 {"EOA", genEOA()}, 55 {"EOAWithPublic", genEOAWithPublicKey()}, 56 {"SCA", genSCA()}, 57 {"SCAWithPublic", genSCAWithPublicKey()}, 58 } 59 testcases := []struct { 60 Name string 61 fn func(t *testing.T, acc Account) 62 }{ 63 {"RLP", testAccountRLP}, 64 {"JSON", testAccountJSON}, 65 } 66 for _, test := range testcases { 67 for _, acc := range accs { 68 Name := test.Name + "/" + acc.Name 69 t.Run(Name, func(t *testing.T) { 70 test.fn(t, acc.acc) 71 }) 72 } 73 } 74 } 75 76 func testAccountRLP(t *testing.T, acc Account) { 77 enc := NewAccountSerializerWithAccount(acc) 78 79 b, err := rlp.EncodeToBytes(enc) 80 if err != nil { 81 panic(err) 82 } 83 84 dec := NewAccountSerializer() 85 86 if err := rlp.DecodeBytes(b, &dec); err != nil { 87 panic(err) 88 } 89 90 if !acc.Equal(dec.account) { 91 fmt.Println("acc") 92 fmt.Println(acc) 93 fmt.Println("dec.account") 94 fmt.Println(dec.account) 95 t.Errorf("acc != dec.account") 96 } 97 } 98 99 func testAccountJSON(t *testing.T, acc Account) { 100 enc := NewAccountSerializerWithAccount(acc) 101 102 b, err := json.Marshal(enc) 103 if err != nil { 104 panic(err) 105 } 106 107 dec := NewAccountSerializer() 108 109 if err := json.Unmarshal(b, &dec); err != nil { 110 panic(err) 111 } 112 113 if !acc.Equal(dec.account) { 114 fmt.Println("acc") 115 fmt.Println(acc) 116 fmt.Println("dec.account") 117 fmt.Println(dec.account) 118 t.Errorf("acc != dec.account") 119 } 120 } 121 122 func genRandomHash() (h common.Hash) { 123 hasher := sha3.NewKeccak256() 124 125 r := rand.Uint64() 126 rlp.Encode(hasher, r) 127 hasher.Sum(h[:0]) 128 129 return h 130 } 131 132 func genEOA() *ExternallyOwnedAccount { 133 humanReadable := false 134 135 return newExternallyOwnedAccountWithMap(map[AccountValueKeyType]interface{}{ 136 AccountValueKeyNonce: rand.Uint64(), 137 AccountValueKeyBalance: big.NewInt(rand.Int63n(10000)), 138 AccountValueKeyHumanReadable: humanReadable, 139 AccountValueKeyAccountKey: accountkey.NewAccountKeyLegacy(), 140 }) 141 } 142 143 func genEOAWithPublicKey() *ExternallyOwnedAccount { 144 humanReadable := false 145 146 k, _ := crypto.GenerateKey() 147 148 return newExternallyOwnedAccountWithMap(map[AccountValueKeyType]interface{}{ 149 AccountValueKeyNonce: rand.Uint64(), 150 AccountValueKeyBalance: big.NewInt(rand.Int63n(10000)), 151 AccountValueKeyHumanReadable: humanReadable, 152 AccountValueKeyAccountKey: accountkey.NewAccountKeyPublicWithValue(&k.PublicKey), 153 }) 154 } 155 156 func genSCA() *SmartContractAccount { 157 humanReadable := false 158 159 return newSmartContractAccountWithMap(map[AccountValueKeyType]interface{}{ 160 AccountValueKeyNonce: rand.Uint64(), 161 AccountValueKeyBalance: big.NewInt(rand.Int63n(10000)), 162 AccountValueKeyHumanReadable: humanReadable, 163 AccountValueKeyAccountKey: accountkey.NewAccountKeyLegacy(), 164 AccountValueKeyStorageRoot: genRandomHash(), 165 AccountValueKeyCodeHash: genRandomHash().Bytes(), 166 AccountValueKeyCodeInfo: params.CodeInfo(0), 167 }) 168 } 169 170 func genSCAWithPublicKey() *SmartContractAccount { 171 humanReadable := false 172 173 k, _ := crypto.GenerateKey() 174 175 return newSmartContractAccountWithMap(map[AccountValueKeyType]interface{}{ 176 AccountValueKeyNonce: rand.Uint64(), 177 AccountValueKeyBalance: big.NewInt(rand.Int63n(10000)), 178 AccountValueKeyHumanReadable: humanReadable, 179 AccountValueKeyAccountKey: accountkey.NewAccountKeyPublicWithValue(&k.PublicKey), 180 AccountValueKeyStorageRoot: genRandomHash(), 181 AccountValueKeyCodeHash: genRandomHash().Bytes(), 182 AccountValueKeyCodeInfo: params.CodeInfo(0), 183 }) 184 } 185 186 // Tests RLP encoding against manually generated strings. 187 func TestSmartContractAccountExt(t *testing.T) { 188 // To create testcases, 189 // - Install https://github.com/ethereumjs/rlp 190 // npm install -g rlp 191 // - In bash, run 192 // maketc(){ echo $(rlp encode "$1")$(rlp encode "$2" | cut -b3-); } 193 // maketc 2 '["0x1234","0x5678"]' 194 var ( 195 commonFields = &AccountCommon{ 196 nonce: 0x1234, 197 balance: big.NewInt(0x5678), 198 humanReadable: false, 199 key: accountkey.NewAccountKeyLegacy(), 200 } 201 hash = common.HexToHash("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff") 202 exthash = common.HexToExtHash("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeffccccddddeeee01") 203 codehash = common.HexToHash("aaaaaaaabbbbbbbbccccccccddddddddaaaaaaaabbbbbbbbccccccccdddddddd").Bytes() 204 codeinfo = params.CodeInfo(0x10) 205 206 // StorageRoot is hash32: maketc 2 '[["0x1234","0x5678","","0x01",[]],"0x00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff","0xaaaaaaaabbbbbbbbccccccccddddddddaaaaaaaabbbbbbbbccccccccdddddddd","0x10"]' 207 scaUnextRLP = "0x02f84dc98212348256788001c0a000112233445566778899aabbccddeeff00112233445566778899aabbccddeeffa0aaaaaaaabbbbbbbbccccccccddddddddaaaaaaaabbbbbbbbccccccccdddddddd10" 208 scaUnext = &SmartContractAccount{ 209 AccountCommon: commonFields, 210 storageRoot: hash.ExtendZero(), 211 codeHash: codehash, 212 codeInfo: codeinfo, 213 } 214 // StorageRoot is exthash: maketc 2 '[["0x1234","0x5678","","0x01",[]],"0x00112233445566778899aabbccddeeff00112233445566778899aabbccddeeffccccddddeeee01","0xaaaaaaaabbbbbbbbccccccccddddddddaaaaaaaabbbbbbbbccccccccdddddddd","0x10"]' 215 scaExtRLP = "0x02f854c98212348256788001c0a700112233445566778899aabbccddeeff00112233445566778899aabbccddeeffccccddddeeee01a0aaaaaaaabbbbbbbbccccccccddddddddaaaaaaaabbbbbbbbccccccccdddddddd10" 216 scaExt = &SmartContractAccount{ 217 AccountCommon: commonFields, 218 storageRoot: exthash, 219 codeHash: codehash, 220 codeInfo: codeinfo, 221 } 222 ) 223 checkEncode := func(account Account, encoded string) { 224 enc := NewAccountSerializerWithAccount(account) 225 b, err := rlp.EncodeToBytes(enc) 226 assert.Nil(t, err) 227 assert.Equal(t, encoded, hexutil.Encode(b)) 228 } 229 checkEncodeExt := func(account Account, encoded string) { 230 enc := NewAccountSerializerExtWithAccount(account) 231 b, err := rlp.EncodeToBytes(enc) 232 assert.Nil(t, err) 233 assert.Equal(t, encoded, hexutil.Encode(b)) 234 } 235 checkEncode(scaUnext, scaUnextRLP) 236 checkEncodeExt(scaUnext, scaUnextRLP) // zero extensions are always unextended 237 238 checkEncode(scaExt, scaUnextRLP) // Regular encoding still results in hash32. Use it for merkle hash. 239 checkEncodeExt(scaExt, scaExtRLP) // Must use SerializeExt to preserve exthash. Use it for disk storage. 240 241 checkDecode := func(encoded string, account Account) { 242 b := common.FromHex(encoded) 243 dec := NewAccountSerializer() 244 err := rlp.DecodeBytes(b, &dec) 245 assert.Nil(t, err) 246 assert.True(t, dec.GetAccount().Equal(account)) 247 } 248 checkDecodeExt := func(encoded string, account Account) { 249 b := common.FromHex(encoded) 250 dec := NewAccountSerializerExt() 251 err := rlp.DecodeBytes(b, &dec) 252 assert.Nil(t, err) 253 assert.True(t, dec.GetAccount().Equal(account)) 254 } 255 256 checkDecode(scaUnextRLP, scaUnext) 257 checkDecodeExt(scaUnextRLP, scaUnext) 258 259 checkDecode(scaExtRLP, scaExt) 260 checkDecodeExt(scaExtRLP, scaExt) 261 } 262 263 func TestUnextendRLP(t *testing.T) { 264 // storage slot 265 testcases := []struct { 266 extended string 267 unextended string 268 }{ 269 { // storage slot (33B) kept as-is 270 "0xa06700000000000000000000000000000000000000000000000000000000000000", 271 "0xa06700000000000000000000000000000000000000000000000000000000000000", 272 }, 273 { // Short EOA (<=ExtHashLength) kept as-is 274 "0x01c98212348256788001c0", 275 "0x01c98212348256788001c0", 276 }, 277 { // Long EOA (>ExtHashLength) kept as-is 278 "0x01ea8212348256788002a1030bc77753515dd61c66df6445ffffbedfc16b6b46c73eb09f01a970cb3bf0a8de", 279 "0x01ea8212348256788002a1030bc77753515dd61c66df6445ffffbedfc16b6b46c73eb09f01a970cb3bf0a8de", 280 }, 281 { // SCA with Hash keps as-is 282 "0x02f84dc98212348256788001c0a000112233445566778899aabbccddeeff00112233445566778899aabbccddeeffa0aaaaaaaabbbbbbbbccccccccddddddddaaaaaaaabbbbbbbbccccccccdddddddd10", 283 "0x02f84dc98212348256788001c0a000112233445566778899aabbccddeeff00112233445566778899aabbccddeeffa0aaaaaaaabbbbbbbbccccccccddddddddaaaaaaaabbbbbbbbccccccccdddddddd10", 284 }, 285 { // SCA with ExtHash unextended 286 "0x02f854c98212348256788001c0a700112233445566778899aabbccddeeff00112233445566778899aabbccddeeffccccddddeeee01a0aaaaaaaabbbbbbbbccccccccddddddddaaaaaaaabbbbbbbbccccccccdddddddd10", 287 "0x02f84dc98212348256788001c0a000112233445566778899aabbccddeeff00112233445566778899aabbccddeeffa0aaaaaaaabbbbbbbbccccccccddddddddaaaaaaaabbbbbbbbccccccccdddddddd10", 288 }, 289 { // Long malform data kept as-is 290 "0xdead0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 291 "0xdead0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 292 }, 293 { // Short malformed data kept as-is 294 "0x80", 295 "0x80", 296 }, 297 { // Short malformed data kept as-is 298 "0x00", 299 "0x00", 300 }, 301 { // Legacy account may crash DecodeRLP, but must not crash UnextendSerializedAccount. 302 "0xf8448080a00000000000000000000000000000000000000000000000000000000000000000a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 303 "0xf8448080a00000000000000000000000000000000000000000000000000000000000000000a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 304 }, 305 } 306 307 for _, tc := range testcases { 308 unextended := UnextendSerializedAccount(common.FromHex(tc.extended)) 309 assert.Equal(t, tc.unextended, hexutil.Encode(unextended)) 310 } 311 }