github.com/koko1123/flow-go-1@v0.29.6/fvm/environment/account_key_updater.go (about) 1 package environment 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 7 "github.com/onflow/cadence/runtime" 8 "github.com/onflow/cadence/runtime/sema" 9 10 "github.com/koko1123/flow-go-1/fvm/crypto" 11 "github.com/koko1123/flow-go-1/fvm/errors" 12 "github.com/koko1123/flow-go-1/fvm/state" 13 "github.com/koko1123/flow-go-1/fvm/tracing" 14 "github.com/koko1123/flow-go-1/model/flow" 15 "github.com/koko1123/flow-go-1/module/trace" 16 fgcrypto "github.com/onflow/flow-go/crypto" 17 fghash "github.com/onflow/flow-go/crypto/hash" 18 ) 19 20 // NewAccountPublicKey construct an account public key given a runtime 21 // public key. 22 func NewAccountPublicKey(publicKey *runtime.PublicKey, 23 hashAlgo sema.HashAlgorithm, 24 keyIndex int, 25 weight int, 26 ) ( 27 *flow.AccountPublicKey, 28 error, 29 ) { 30 31 var err error 32 signAlgorithm := crypto.RuntimeToCryptoSigningAlgorithm(publicKey.SignAlgo) 33 if signAlgorithm != fgcrypto.ECDSAP256 && 34 signAlgorithm != fgcrypto.ECDSASecp256k1 { 35 36 return nil, fmt.Errorf( 37 "adding account key failed: %w", 38 errors.NewValueErrorf( 39 fmt.Sprintf("%d", publicKey.SignAlgo), 40 "signature algorithm type not supported")) 41 } 42 43 hashAlgorithm := crypto.RuntimeToCryptoHashingAlgorithm(hashAlgo) 44 if hashAlgorithm != fghash.SHA2_256 && 45 hashAlgorithm != fghash.SHA3_256 { 46 47 return nil, fmt.Errorf( 48 "adding account key failed: %w", 49 errors.NewValueErrorf( 50 fmt.Sprintf("%d", hashAlgo), 51 "hashing algorithm type not supported")) 52 } 53 54 decodedPublicKey, err := fgcrypto.DecodePublicKey( 55 signAlgorithm, 56 publicKey.PublicKey) 57 if err != nil { 58 return nil, fmt.Errorf( 59 "adding account key failed: %w", 60 errors.NewValueErrorf( 61 hex.EncodeToString(publicKey.PublicKey), 62 "cannot decode public key: %w", 63 err)) 64 } 65 66 return &flow.AccountPublicKey{ 67 Index: keyIndex, 68 PublicKey: decodedPublicKey, 69 SignAlgo: signAlgorithm, 70 HashAlgo: hashAlgorithm, 71 SeqNumber: 0, 72 Weight: weight, 73 Revoked: false, 74 }, nil 75 } 76 77 // AccountKeyUpdater handles all account keys modification. 78 // 79 // Note that scripts cannot modify account keys, but must expose the API in 80 // compliance with the runtime environment interface. 81 type AccountKeyUpdater interface { 82 // AddEncodedAccountKey adds an encoded public key to an existing account. 83 // 84 // This function returns an error if the specified account does not exist or 85 // if the key insertion fails. 86 // 87 // Note that the script variant will return OperationNotSupportedError. 88 AddEncodedAccountKey(address runtime.Address, publicKey []byte) error 89 90 // RevokeEncodedAccountKey revokes a public key by index from an existing 91 // account. 92 // 93 // This function returns an error if the specified account does not exist, 94 // the provided key is invalid, or if key revoking fails. 95 // 96 // Note that the script variant will return OperationNotSupportedError. 97 RevokeEncodedAccountKey( 98 address runtime.Address, 99 index int, 100 ) ( 101 []byte, 102 error, 103 ) 104 105 // AddAccountKey adds a public key to an existing account. 106 // 107 // This function returns an error if the specified account does not exist or 108 // if the key insertion fails. 109 // 110 // Note that the script variant will return OperationNotSupportedError. 111 AddAccountKey( 112 address runtime.Address, 113 publicKey *runtime.PublicKey, 114 hashAlgo runtime.HashAlgorithm, 115 weight int, 116 ) ( 117 *runtime.AccountKey, 118 error, 119 ) 120 121 // RevokeAccountKey revokes a public key by index from an existing account, 122 // and returns the revoked key. 123 // 124 // This function returns a nil key with no errors, if a key doesn't exist 125 // at the given index. An error is returned if the specified account does 126 // not exist, the provided index is not valid, or if the key revoking 127 // fails. 128 // 129 // Note that the script variant will return OperationNotSupportedError. 130 RevokeAccountKey( 131 address runtime.Address, 132 keyIndex int, 133 ) ( 134 *runtime.AccountKey, 135 error, 136 ) 137 } 138 139 type ParseRestrictedAccountKeyUpdater struct { 140 txnState *state.TransactionState 141 impl AccountKeyUpdater 142 } 143 144 func NewParseRestrictedAccountKeyUpdater( 145 txnState *state.TransactionState, 146 impl AccountKeyUpdater, 147 ) ParseRestrictedAccountKeyUpdater { 148 return ParseRestrictedAccountKeyUpdater{ 149 txnState: txnState, 150 impl: impl, 151 } 152 } 153 154 func (updater ParseRestrictedAccountKeyUpdater) AddEncodedAccountKey( 155 address runtime.Address, 156 publicKey []byte, 157 ) error { 158 return parseRestrict2Arg( 159 updater.txnState, 160 trace.FVMEnvAddEncodedAccountKey, 161 updater.impl.AddEncodedAccountKey, 162 address, 163 publicKey) 164 } 165 166 func (updater ParseRestrictedAccountKeyUpdater) RevokeEncodedAccountKey( 167 address runtime.Address, 168 index int, 169 ) ( 170 []byte, 171 error, 172 ) { 173 return parseRestrict2Arg1Ret( 174 updater.txnState, 175 trace.FVMEnvRevokeEncodedAccountKey, 176 updater.impl.RevokeEncodedAccountKey, 177 address, 178 index) 179 } 180 181 func (updater ParseRestrictedAccountKeyUpdater) AddAccountKey( 182 address runtime.Address, 183 publicKey *runtime.PublicKey, 184 hashAlgo runtime.HashAlgorithm, 185 weight int, 186 ) ( 187 *runtime.AccountKey, 188 error, 189 ) { 190 return parseRestrict4Arg1Ret( 191 updater.txnState, 192 trace.FVMEnvAddAccountKey, 193 updater.impl.AddAccountKey, 194 address, 195 publicKey, 196 hashAlgo, 197 weight) 198 } 199 200 func (updater ParseRestrictedAccountKeyUpdater) RevokeAccountKey( 201 address runtime.Address, 202 keyIndex int, 203 ) ( 204 *runtime.AccountKey, 205 error, 206 ) { 207 return parseRestrict2Arg1Ret( 208 updater.txnState, 209 trace.FVMEnvRevokeAccountKey, 210 updater.impl.RevokeAccountKey, 211 address, 212 keyIndex) 213 } 214 215 type NoAccountKeyUpdater struct{} 216 217 func (NoAccountKeyUpdater) AddEncodedAccountKey( 218 address runtime.Address, 219 publicKey []byte, 220 ) error { 221 return errors.NewOperationNotSupportedError("AddEncodedAccountKey") 222 } 223 224 func (NoAccountKeyUpdater) RevokeEncodedAccountKey( 225 address runtime.Address, 226 index int, 227 ) ( 228 []byte, 229 error, 230 ) { 231 return nil, errors.NewOperationNotSupportedError("RevokeEncodedAccountKey") 232 } 233 234 func (NoAccountKeyUpdater) AddAccountKey( 235 address runtime.Address, 236 publicKey *runtime.PublicKey, 237 hashAlgo runtime.HashAlgorithm, 238 weight int, 239 ) ( 240 *runtime.AccountKey, 241 error, 242 ) { 243 return nil, errors.NewOperationNotSupportedError("AddAccountKey") 244 } 245 246 func (NoAccountKeyUpdater) RevokeAccountKey( 247 address runtime.Address, 248 keyIndex int, 249 ) ( 250 *runtime.AccountKey, 251 error, 252 ) { 253 return nil, errors.NewOperationNotSupportedError("RevokeAccountKey") 254 } 255 256 type accountKeyUpdater struct { 257 tracer tracing.TracerSpan 258 meter Meter 259 260 accounts Accounts 261 txnState *state.TransactionState 262 env Environment 263 } 264 265 func NewAccountKeyUpdater( 266 tracer tracing.TracerSpan, 267 meter Meter, 268 accounts Accounts, 269 txnState *state.TransactionState, 270 env Environment, 271 ) *accountKeyUpdater { 272 return &accountKeyUpdater{ 273 tracer: tracer, 274 meter: meter, 275 accounts: accounts, 276 txnState: txnState, 277 env: env, 278 } 279 } 280 281 // AddAccountKey adds a public key to an existing account. 282 // 283 // This function returns an error if the specified account does not exist or 284 // if the key insertion fails. 285 func (updater *accountKeyUpdater) addAccountKey( 286 address runtime.Address, 287 publicKey *runtime.PublicKey, 288 hashAlgo runtime.HashAlgorithm, 289 weight int, 290 ) ( 291 *runtime.AccountKey, 292 error, 293 ) { 294 accountAddress := flow.Address(address) 295 296 ok, err := updater.accounts.Exists(accountAddress) 297 if err != nil { 298 return nil, fmt.Errorf("adding account key failed: %w", err) 299 } 300 if !ok { 301 return nil, fmt.Errorf( 302 "adding account key failed: %w", 303 errors.NewAccountNotFoundError(accountAddress)) 304 } 305 306 keyIndex, err := updater.accounts.GetPublicKeyCount(accountAddress) 307 if err != nil { 308 return nil, fmt.Errorf("adding account key failed: %w", err) 309 } 310 311 accountPublicKey, err := NewAccountPublicKey( 312 publicKey, 313 hashAlgo, 314 int(keyIndex), 315 weight) 316 if err != nil { 317 return nil, fmt.Errorf("adding account key failed: %w", err) 318 } 319 320 err = updater.accounts.AppendPublicKey(accountAddress, *accountPublicKey) 321 if err != nil { 322 return nil, fmt.Errorf("adding account key failed: %w", err) 323 } 324 325 return &runtime.AccountKey{ 326 KeyIndex: accountPublicKey.Index, 327 PublicKey: publicKey, 328 HashAlgo: hashAlgo, 329 Weight: accountPublicKey.Weight, 330 IsRevoked: accountPublicKey.Revoked, 331 }, nil 332 } 333 334 // RevokeAccountKey revokes a public key by index from an existing account, 335 // and returns the revoked key. 336 // 337 // This function returns a nil key with no errors, if a key doesn't exist at 338 // the given index. An error is returned if the specified account does not 339 // exist, the provided index is not valid, or if the key revoking fails. 340 // 341 // TODO (ramtin) do we have to return runtime.AccountKey for this method or 342 // can be separated into another method 343 func (updater *accountKeyUpdater) revokeAccountKey( 344 address runtime.Address, 345 keyIndex int, 346 ) ( 347 *runtime.AccountKey, 348 error, 349 ) { 350 accountAddress := flow.Address(address) 351 352 ok, err := updater.accounts.Exists(accountAddress) 353 if err != nil { 354 return nil, fmt.Errorf("revoking account key failed: %w", err) 355 } 356 357 if !ok { 358 return nil, fmt.Errorf( 359 "revoking account key failed: %w", 360 errors.NewAccountNotFoundError(accountAddress)) 361 } 362 363 // Don't return an error for invalid key indices 364 if keyIndex < 0 { 365 return nil, nil 366 } 367 368 var publicKey flow.AccountPublicKey 369 publicKey, err = updater.accounts.GetPublicKey( 370 accountAddress, 371 uint64(keyIndex)) 372 if err != nil { 373 // If a key is not found at a given index, then return a nil key with 374 // no errors. This is to be inline with the Cadence runtime. Otherwise 375 // Cadence runtime cannot distinguish between a 'key not found error' 376 // vs other internal errors. 377 if errors.IsAccountAccountPublicKeyNotFoundError(err) { 378 return nil, nil 379 } 380 return nil, fmt.Errorf("revoking account key failed: %w", err) 381 } 382 383 // mark this key as revoked 384 publicKey.Revoked = true 385 386 _, err = updater.accounts.SetPublicKey( 387 accountAddress, 388 uint64(keyIndex), 389 publicKey) 390 if err != nil { 391 return nil, fmt.Errorf("revoking account key failed: %w", err) 392 } 393 394 // Prepare account key to return 395 signAlgo := crypto.CryptoToRuntimeSigningAlgorithm(publicKey.SignAlgo) 396 if signAlgo == runtime.SignatureAlgorithmUnknown { 397 return nil, fmt.Errorf( 398 "revoking account key failed: %w", 399 errors.NewValueErrorf( 400 publicKey.SignAlgo.String(), 401 "signature algorithm type not found")) 402 } 403 404 hashAlgo := crypto.CryptoToRuntimeHashingAlgorithm(publicKey.HashAlgo) 405 if hashAlgo == runtime.HashAlgorithmUnknown { 406 return nil, fmt.Errorf( 407 "revoking account key failed: %w", 408 errors.NewValueErrorf( 409 publicKey.HashAlgo.String(), 410 "hashing algorithm type not found")) 411 } 412 413 return &runtime.AccountKey{ 414 KeyIndex: publicKey.Index, 415 PublicKey: &runtime.PublicKey{ 416 PublicKey: publicKey.PublicKey.Encode(), 417 SignAlgo: signAlgo, 418 }, 419 HashAlgo: hashAlgo, 420 Weight: publicKey.Weight, 421 IsRevoked: publicKey.Revoked, 422 }, nil 423 } 424 425 // InternalAddEncodedAccountKey adds an encoded public key to an existing 426 // account. 427 // 428 // This function returns following error 429 // * NewAccountNotFoundError - if the specified account does not exist 430 // * ValueError - if the provided encodedPublicKey is not valid public key 431 func (updater *accountKeyUpdater) InternalAddEncodedAccountKey( 432 address runtime.Address, 433 encodedPublicKey []byte, 434 ) error { 435 accountAddress := flow.Address(address) 436 437 ok, err := updater.accounts.Exists(accountAddress) 438 if err != nil { 439 return fmt.Errorf("adding encoded account key failed: %w", err) 440 } 441 442 if !ok { 443 return errors.NewAccountNotFoundError(accountAddress) 444 } 445 446 var publicKey flow.AccountPublicKey 447 448 publicKey, err = flow.DecodeRuntimeAccountPublicKey(encodedPublicKey, 0) 449 if err != nil { 450 hexEncodedPublicKey := hex.EncodeToString(encodedPublicKey) 451 return fmt.Errorf( 452 "adding encoded account key failed: %w", 453 errors.NewValueErrorf( 454 hexEncodedPublicKey, 455 "invalid encoded public key value: %w", 456 err)) 457 } 458 459 err = updater.accounts.AppendPublicKey(accountAddress, publicKey) 460 if err != nil { 461 return fmt.Errorf("adding encoded account key failed: %w", err) 462 } 463 464 return nil 465 } 466 467 // RemoveAccountKey revokes a public key by index from an existing account. 468 // 469 // This function returns an error if the specified account does not exist, the 470 // provided key is invalid, or if key revoking fails. 471 func (updater *accountKeyUpdater) removeAccountKey( 472 address runtime.Address, 473 keyIndex int, 474 ) ( 475 []byte, 476 error, 477 ) { 478 accountAddress := flow.Address(address) 479 480 ok, err := updater.accounts.Exists(accountAddress) 481 if err != nil { 482 return nil, fmt.Errorf("remove account key failed: %w", err) 483 } 484 485 if !ok { 486 issue := errors.NewAccountNotFoundError(accountAddress) 487 return nil, fmt.Errorf("remove account key failed: %w", issue) 488 } 489 490 if keyIndex < 0 { 491 err = errors.NewValueErrorf( 492 fmt.Sprint(keyIndex), 493 "key index must be positive") 494 return nil, fmt.Errorf("remove account key failed: %w", err) 495 } 496 497 var publicKey flow.AccountPublicKey 498 publicKey, err = updater.accounts.GetPublicKey( 499 accountAddress, 500 uint64(keyIndex)) 501 if err != nil { 502 return nil, fmt.Errorf("remove account key failed: %w", err) 503 } 504 505 // mark this key as revoked 506 publicKey.Revoked = true 507 508 encodedPublicKey, err := updater.accounts.SetPublicKey( 509 accountAddress, 510 uint64(keyIndex), 511 publicKey) 512 if err != nil { 513 return nil, fmt.Errorf("remove account key failed: %w", err) 514 } 515 516 return encodedPublicKey, nil 517 } 518 519 func (updater *accountKeyUpdater) AddEncodedAccountKey( 520 address runtime.Address, 521 publicKey []byte, 522 ) error { 523 defer updater.tracer.StartChildSpan( 524 trace.FVMEnvAddEncodedAccountKey).End() 525 526 err := updater.meter.MeterComputation( 527 ComputationKindAddEncodedAccountKey, 528 1) 529 if err != nil { 530 return fmt.Errorf("add encoded account key failed: %w", err) 531 } 532 533 err = updater.accounts.CheckAccountNotFrozen(flow.Address(address)) 534 if err != nil { 535 return fmt.Errorf("add encoded account key failed: %w", err) 536 } 537 538 // TODO do a call to track the computation usage and memory usage 539 // 540 // don't enforce limit during adding a key 541 updater.txnState.RunWithAllLimitsDisabled(func() { 542 err = updater.InternalAddEncodedAccountKey(address, publicKey) 543 }) 544 545 if err != nil { 546 return fmt.Errorf("add encoded account key failed: %w", err) 547 } 548 return nil 549 } 550 551 func (updater *accountKeyUpdater) RevokeEncodedAccountKey( 552 address runtime.Address, 553 index int, 554 ) ( 555 []byte, 556 error, 557 ) { 558 defer updater.tracer.StartChildSpan(trace.FVMEnvRevokeEncodedAccountKey).End() 559 560 err := updater.meter.MeterComputation( 561 ComputationKindRevokeEncodedAccountKey, 562 1) 563 if err != nil { 564 return nil, fmt.Errorf("revoke encoded account key failed: %w", err) 565 } 566 567 err = updater.accounts.CheckAccountNotFrozen(flow.Address(address)) 568 if err != nil { 569 return nil, fmt.Errorf("revoke encoded account key failed: %w", err) 570 } 571 572 encodedKey, err := updater.removeAccountKey(address, index) 573 if err != nil { 574 return nil, fmt.Errorf("revoke encoded account key failed: %w", err) 575 } 576 577 return encodedKey, nil 578 } 579 580 func (updater *accountKeyUpdater) AddAccountKey( 581 address runtime.Address, 582 publicKey *runtime.PublicKey, 583 hashAlgo runtime.HashAlgorithm, 584 weight int, 585 ) ( 586 *runtime.AccountKey, 587 error, 588 ) { 589 defer updater.tracer.StartChildSpan(trace.FVMEnvAddAccountKey).End() 590 591 err := updater.meter.MeterComputation( 592 ComputationKindAddAccountKey, 593 1) 594 if err != nil { 595 return nil, fmt.Errorf("add account key failed: %w", err) 596 } 597 598 accKey, err := updater.addAccountKey( 599 address, 600 publicKey, 601 hashAlgo, 602 weight) 603 if err != nil { 604 return nil, fmt.Errorf("add account key failed: %w", err) 605 } 606 607 return accKey, nil 608 } 609 610 func (updater *accountKeyUpdater) RevokeAccountKey( 611 address runtime.Address, 612 keyIndex int, 613 ) ( 614 *runtime.AccountKey, 615 error, 616 ) { 617 defer updater.tracer.StartChildSpan(trace.FVMEnvRevokeAccountKey).End() 618 619 err := updater.meter.MeterComputation( 620 ComputationKindRevokeAccountKey, 621 1) 622 if err != nil { 623 return nil, fmt.Errorf("revoke account key failed: %w", err) 624 } 625 626 return updater.revokeAccountKey(address, keyIndex) 627 }