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