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  }